FEATURE: Allow setting avatar flair for automatic groups (#12586)
This commit is contained in:
parent
0052fcf7c4
commit
e8a9917db1
|
@ -0,0 +1,38 @@
|
|||
import MountWidget from "discourse/components/mount-widget";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import autoGroupFlairForUser from "discourse/lib/avatar-flair";
|
||||
|
||||
export default MountWidget.extend({
|
||||
widget: "avatar-flair",
|
||||
|
||||
@observes("user")
|
||||
_rerender() {
|
||||
this.queueRerender();
|
||||
},
|
||||
|
||||
buildArgs() {
|
||||
if (!this.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.user.primary_group_flair_url) {
|
||||
return {
|
||||
primary_group_flair_url: this.user.primary_group_flair_url,
|
||||
primary_group_flair_bg_color: this.user.primary_group_flair_bg_color,
|
||||
primary_group_flair_color: this.user.primary_group_flair_color,
|
||||
primary_group_name: this.user.primary_group_name,
|
||||
};
|
||||
} else {
|
||||
const autoFlairAttrs = autoGroupFlairForUser(this.site, this.user);
|
||||
if (autoFlairAttrs) {
|
||||
return {
|
||||
primary_group_flair_url: autoFlairAttrs.primary_group_flair_url,
|
||||
primary_group_flair_bg_color:
|
||||
autoFlairAttrs.primary_group_flair_bg_color,
|
||||
primary_group_flair_color: autoFlairAttrs.primary_group_flair_color,
|
||||
primary_group_name: autoFlairAttrs.primary_group_name,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
let _autoGroupFlair, _noAutoFlair;
|
||||
|
||||
export default function autoGroupFlairForUser(site, user) {
|
||||
if (!_autoGroupFlair) {
|
||||
initializeAutoGroupFlair(site);
|
||||
}
|
||||
|
||||
if (_noAutoFlair) {
|
||||
// No automatic groups have flair.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.admin && _autoGroupFlair.admins) {
|
||||
return _autoGroupFlair.admins;
|
||||
}
|
||||
|
||||
if (user.moderator && _autoGroupFlair.moderators) {
|
||||
return _autoGroupFlair.moderators;
|
||||
}
|
||||
|
||||
if (_autoGroupFlair.staff && (user.admin || user.moderator)) {
|
||||
return _autoGroupFlair.staff;
|
||||
}
|
||||
|
||||
let trustLevel = user.trust_level || user.trustLevel;
|
||||
|
||||
if (trustLevel) {
|
||||
for (let i = trustLevel; i >= 0; i--) {
|
||||
if (_autoGroupFlair[`trust_level_${i}`]) {
|
||||
return _autoGroupFlair[`trust_level_${i}`];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function resetFlair() {
|
||||
_autoGroupFlair = null;
|
||||
_noAutoFlair = null;
|
||||
}
|
||||
|
||||
function initializeAutoGroupFlair(site) {
|
||||
_autoGroupFlair = {};
|
||||
_noAutoFlair = true;
|
||||
|
||||
[
|
||||
"admins",
|
||||
"moderators",
|
||||
"staff",
|
||||
"trust_level_0",
|
||||
"trust_level_1",
|
||||
"trust_level_2",
|
||||
"trust_level_3",
|
||||
"trust_level_4",
|
||||
].forEach((groupName) => {
|
||||
const group = site.groups.findBy("name", groupName);
|
||||
if (group && group.flair_url) {
|
||||
_noAutoFlair = false;
|
||||
_autoGroupFlair[groupName] = {
|
||||
primary_group_flair_url: group.flair_url,
|
||||
primary_group_flair_bg_color: group.flair_bg_color,
|
||||
primary_group_flair_color: group.flair_color,
|
||||
primary_group_name: group.name.replace(/_/g, " "),
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
|
@ -81,6 +81,7 @@ export function transformBasicPost(post) {
|
|||
userCustomFields: post.user_custom_fields,
|
||||
readCount: post.readers_count,
|
||||
canPublishPage: false,
|
||||
trustLevel: post.trust_level,
|
||||
};
|
||||
|
||||
_additionalAttributes.forEach((a) => (postAtts[a] = post[a]));
|
||||
|
|
|
@ -26,6 +26,12 @@
|
|||
{{d-editor value=model.bio_raw class="group-form-bio input-xxlarge"}}
|
||||
</div>
|
||||
|
||||
{{#if model.automatic}}
|
||||
<div class="control-group">
|
||||
{{group-flair-inputs model=model}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if canEdit}}
|
||||
{{yield}}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
{{#user-link user=topic.lastPosterUser}}
|
||||
{{avatar topic.lastPosterUser imageSize="large"}}
|
||||
{{/user-link}}
|
||||
{{user-avatar-flair user=topic.lastPosterUser}}
|
||||
{{#if topic.lastPosterGroup}}
|
||||
{{avatar-flair
|
||||
flairURL=topic.lastPosterGroup.flair_url
|
||||
|
|
|
@ -27,13 +27,9 @@
|
|||
{{else}}
|
||||
<a href={{this.user.path}} {{action "showUser" this.user}} class="card-huge-avatar">{{bound-avatar this.user "huge"}}</a>
|
||||
{{/if}}
|
||||
{{#if this.user.primary_group_name}}
|
||||
{{avatar-flair
|
||||
flairURL=this.user.primary_group_flair_url
|
||||
flairBgColor=this.user.primary_group_flair_bg_color
|
||||
flairColor=this.user.primary_group_flair_color
|
||||
groupName=this.user.primary_group_name}}
|
||||
{{/if}}
|
||||
|
||||
{{user-avatar-flair user=this.user}}
|
||||
|
||||
{{plugin-outlet name="user-card-avatar-flair" args=(hash user=this.user) tagName="div"}}
|
||||
</div>
|
||||
<div class="names">
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<div class="user-image">
|
||||
<div class="user-image-inner">
|
||||
<a href={{this.userPath}} data-user-card={{@user.username}}>{{avatar @user imageSize="large"}}</a>
|
||||
{{#if @user.primary_group_name}}
|
||||
{{avatar-flair
|
||||
flairURL=@user.primary_group_flair_url
|
||||
flairBgColor=@user.primary_group_flair_bg_color
|
||||
flairColor=@user.primary_group_flair_color
|
||||
groupName=@user.primary_group_name}}
|
||||
{{/if}}
|
||||
{{user-avatar-flair user=@user}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<div class="user-profile-avatar">
|
||||
{{bound-avatar @user "huge"}}
|
||||
{{#if @user.primary_group_name}}
|
||||
{{avatar-flair
|
||||
flairURL=@user.primary_group_flair_url
|
||||
flairBgColor=@user.primary_group_flair_bg_color
|
||||
flairColor=@user.primary_group_flair_color
|
||||
groupName=@user.primary_group_name}}
|
||||
{{/if}}
|
||||
{{user-avatar-flair user=@user}}
|
||||
{{plugin-outlet name="user-profile-avatar-flair" args=(hash model=@user) tagName="div"}}
|
||||
</div>
|
||||
|
|
|
@ -20,6 +20,7 @@ import { postTransformCallbacks } from "discourse/widgets/post-stream";
|
|||
import { prioritizeNameInUx } from "discourse/lib/settings";
|
||||
import { relativeAgeMediumSpan } from "discourse/lib/formatter";
|
||||
import { transformBasicPost } from "discourse/lib/transform-post";
|
||||
import autoGroupFlairForUser from "discourse/lib/avatar-flair";
|
||||
|
||||
function transformWithCallbacks(post) {
|
||||
let transformed = transformBasicPost(post);
|
||||
|
@ -187,6 +188,11 @@ createWidget("post-avatar", {
|
|||
|
||||
if (attrs.primary_group_flair_url || attrs.primary_group_flair_bg_color) {
|
||||
result.push(this.attach("avatar-flair", attrs));
|
||||
} else {
|
||||
const autoFlairAttrs = autoGroupFlairForUser(this.site, attrs);
|
||||
if (autoFlairAttrs) {
|
||||
result.push(this.attach("avatar-flair", autoFlairAttrs));
|
||||
}
|
||||
}
|
||||
|
||||
result.push(h("div.poster-avatar-extra"));
|
||||
|
|
|
@ -4,6 +4,7 @@ import I18n from "I18n";
|
|||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { h } from "virtual-dom";
|
||||
import { replaceEmoji } from "discourse/widgets/emoji";
|
||||
import autoGroupFlairForUser from "discourse/lib/avatar-flair";
|
||||
|
||||
const LINKS_SHOWN = 5;
|
||||
|
||||
|
@ -61,6 +62,11 @@ createWidget("topic-participant", {
|
|||
|
||||
if (attrs.primary_group_flair_url || attrs.primary_group_flair_bg_color) {
|
||||
linkContents.push(this.attach("avatar-flair", attrs));
|
||||
} else {
|
||||
const autoFlairAttrs = autoGroupFlairForUser(this.site, attrs);
|
||||
if (autoFlairAttrs) {
|
||||
linkContents.push(this.attach("avatar-flair", autoFlairAttrs));
|
||||
}
|
||||
}
|
||||
|
||||
return h(
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import componentTest, {
|
||||
setupRenderingTest,
|
||||
} from "discourse/tests/helpers/component-test";
|
||||
import {
|
||||
discourseModule,
|
||||
queryAll,
|
||||
} from "discourse/tests/helpers/qunit-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { resetFlair } from "discourse/lib/avatar-flair";
|
||||
|
||||
function setupSiteGroups(that) {
|
||||
that.site.groups = [
|
||||
{
|
||||
id: 1,
|
||||
name: "admins",
|
||||
flair_url: "fa-bars",
|
||||
flair_bg_color: "CC000A",
|
||||
flair_color: "FFFFFA",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "staff",
|
||||
flair_url: "fa-bars",
|
||||
flair_bg_color: "CC0005",
|
||||
flair_color: "FFFFF5",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "trust_level_1",
|
||||
flair_url: "fa-dice-one",
|
||||
flair_bg_color: "CC0001",
|
||||
flair_color: "FFFFF1",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "trust_level_2",
|
||||
flair_url: "fa-dice-two",
|
||||
flair_bg_color: "CC0002",
|
||||
flair_color: "FFFFF2",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
discourseModule(
|
||||
"Integration | Component | user-avatar-flair",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
componentTest("avatar flair for admin user", {
|
||||
template: hbs`{{user-avatar-flair user=args}}`,
|
||||
beforeEach() {
|
||||
resetFlair();
|
||||
this.set("args", {
|
||||
admin: true,
|
||||
moderator: false,
|
||||
trust_level: 2,
|
||||
});
|
||||
setupSiteGroups(this);
|
||||
},
|
||||
afterEach() {
|
||||
resetFlair();
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(queryAll(".avatar-flair").length, "it has the tag");
|
||||
assert.ok(queryAll("svg.d-icon-bars").length, "it has the svg icon");
|
||||
assert.equal(
|
||||
queryAll(".avatar-flair").attr("style"),
|
||||
"background-color: #CC000A; color: #FFFFFA; ",
|
||||
"it has styles"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("avatar flair for moderator user with fallback to staff", {
|
||||
template: hbs`{{user-avatar-flair user=args}}`,
|
||||
beforeEach() {
|
||||
resetFlair();
|
||||
this.set("args", {
|
||||
admin: false,
|
||||
moderator: true,
|
||||
trust_level: 2,
|
||||
});
|
||||
setupSiteGroups(this);
|
||||
},
|
||||
afterEach() {
|
||||
resetFlair();
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(queryAll(".avatar-flair").length, "it has the tag");
|
||||
assert.ok(queryAll("svg.d-icon-bars").length, "it has the svg icon");
|
||||
assert.equal(
|
||||
queryAll(".avatar-flair").attr("style"),
|
||||
"background-color: #CC0005; color: #FFFFF5; ",
|
||||
"it has styles"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("avatar flair for trust level", {
|
||||
template: hbs`{{user-avatar-flair user=args}}`,
|
||||
beforeEach() {
|
||||
resetFlair();
|
||||
this.set("args", {
|
||||
admin: false,
|
||||
moderator: false,
|
||||
trust_level: 2,
|
||||
});
|
||||
setupSiteGroups(this);
|
||||
},
|
||||
afterEach() {
|
||||
resetFlair();
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(queryAll(".avatar-flair").length, "it has the tag");
|
||||
assert.ok(
|
||||
queryAll("svg.d-icon-dice-two").length,
|
||||
"it has the svg icon"
|
||||
);
|
||||
assert.equal(
|
||||
queryAll(".avatar-flair").attr("style"),
|
||||
"background-color: #CC0002; color: #FFFFF2; ",
|
||||
"it has styles"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("avatar flair for trust level with fallback", {
|
||||
template: hbs`{{user-avatar-flair user=args}}`,
|
||||
beforeEach() {
|
||||
resetFlair();
|
||||
this.set("args", {
|
||||
admin: false,
|
||||
moderator: false,
|
||||
trust_level: 3,
|
||||
});
|
||||
setupSiteGroups(this);
|
||||
},
|
||||
afterEach() {
|
||||
resetFlair();
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(queryAll(".avatar-flair").length, "it has the tag");
|
||||
assert.ok(
|
||||
queryAll("svg.d-icon-dice-two").length,
|
||||
"it has the svg icon"
|
||||
);
|
||||
assert.equal(
|
||||
queryAll(".avatar-flair").attr("style"),
|
||||
"background-color: #CC0002; color: #FFFFF2; ",
|
||||
"it has styles"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
componentTest("avatar flair for primary group flair", {
|
||||
template: hbs`{{user-avatar-flair user=args}}`,
|
||||
beforeEach() {
|
||||
resetFlair();
|
||||
this.set("args", {
|
||||
admin: false,
|
||||
moderator: false,
|
||||
trust_level: 3,
|
||||
primary_group_flair_url: "fa-times",
|
||||
primary_group_flair_bg_color: "123456",
|
||||
primary_group_flair_color: "B0B0B0",
|
||||
primary_group_name: "Band Geeks",
|
||||
});
|
||||
setupSiteGroups(this);
|
||||
},
|
||||
afterEach() {
|
||||
resetFlair();
|
||||
},
|
||||
test(assert) {
|
||||
assert.ok(queryAll(".avatar-flair").length, "it has the tag");
|
||||
assert.ok(queryAll("svg.d-icon-times").length, "it has the svg icon");
|
||||
assert.equal(
|
||||
queryAll(".avatar-flair").attr("style"),
|
||||
"background-color: #123456; color: #B0B0B0; ",
|
||||
"it has styles"
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
|
@ -579,6 +579,10 @@ class GroupsController < ApplicationController
|
|||
messageable_level
|
||||
default_notification_level
|
||||
bio_raw
|
||||
flair_icon
|
||||
flair_upload_id
|
||||
flair_bg_color
|
||||
flair_color
|
||||
}
|
||||
else
|
||||
default_params = %i{
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class CategoryAndTopicListsSerializer < ApplicationSerializer
|
||||
has_one :category_list, serializer: CategoryListSerializer, embed: :objects
|
||||
has_one :topic_list, serializer: TopicListSerializer, embed: :objects
|
||||
has_many :users, serializer: BasicUserSerializer, embed: :objects
|
||||
has_many :users, serializer: PosterSerializer, embed: :objects
|
||||
has_many :primary_groups, serializer: PrimaryGroupSerializer, embed: :objects
|
||||
|
||||
def users
|
||||
|
|
|
@ -6,7 +6,10 @@ module UserPrimaryGroupMixin
|
|||
klass.attributes :primary_group_name,
|
||||
:primary_group_flair_url,
|
||||
:primary_group_flair_bg_color,
|
||||
:primary_group_flair_color
|
||||
:primary_group_flair_color,
|
||||
:admin,
|
||||
:moderator,
|
||||
:trust_level
|
||||
end
|
||||
|
||||
def primary_group_name
|
||||
|
@ -41,4 +44,19 @@ module UserPrimaryGroupMixin
|
|||
object&.primary_group&.flair_color.present?
|
||||
end
|
||||
|
||||
def include_admin?
|
||||
object&.admin
|
||||
end
|
||||
|
||||
def admin
|
||||
true
|
||||
end
|
||||
|
||||
def include_moderator?
|
||||
object&.moderator
|
||||
end
|
||||
|
||||
def moderator
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PosterSerializer < BasicUserSerializer
|
||||
include UserPrimaryGroupMixin
|
||||
end
|
|
@ -62,7 +62,17 @@ class SiteSerializer < ApplicationSerializer
|
|||
|
||||
def groups
|
||||
cache_anon_fragment("group_names") do
|
||||
object.groups.order(:name).pluck(:id, :name).map { |id, name| { id: id, name: name } }.as_json
|
||||
object.groups.order(:name)
|
||||
.select(:id, :name, :flair_icon, :flair_upload_id, :flair_bg_color, :flair_color)
|
||||
.map do |g|
|
||||
{
|
||||
id: g.id,
|
||||
name: g.name,
|
||||
flair_url: g.flair_url,
|
||||
flair_bg_color: g.flair_bg_color,
|
||||
flair_color: g.flair_color,
|
||||
}
|
||||
end.as_json
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
class TopicPostCountSerializer < BasicUserSerializer
|
||||
|
||||
attributes :post_count, :primary_group_name,
|
||||
:primary_group_flair_url, :primary_group_flair_color, :primary_group_flair_bg_color
|
||||
:primary_group_flair_url, :primary_group_flair_color, :primary_group_flair_bg_color,
|
||||
:admin, :moderator, :trust_level,
|
||||
|
||||
def id
|
||||
object[:user].id
|
||||
|
@ -34,4 +35,24 @@ class TopicPostCountSerializer < BasicUserSerializer
|
|||
object[:user]&.primary_group&.flair_color
|
||||
end
|
||||
|
||||
def include_admin?
|
||||
object[:user].admin
|
||||
end
|
||||
|
||||
def admin
|
||||
true
|
||||
end
|
||||
|
||||
def include_moderator?
|
||||
object[:user].moderator
|
||||
end
|
||||
|
||||
def moderator
|
||||
true
|
||||
end
|
||||
|
||||
def trust_level
|
||||
object[:user].trust_level
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class TopicPosterSerializer < ApplicationSerializer
|
||||
attributes :extras, :description
|
||||
has_one :user, serializer: BasicUserSerializer
|
||||
|
||||
has_one :user, serializer: PosterSerializer
|
||||
has_one :primary_group, serializer: PrimaryGroupSerializer
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@ class UserLookup
|
|||
private
|
||||
|
||||
def self.lookup_columns
|
||||
@user_lookup_columns ||= %i{id username name uploaded_avatar_id primary_group_id}
|
||||
@user_lookup_columns ||= %i{id username name uploaded_avatar_id primary_group_id admin moderator trust_level}
|
||||
end
|
||||
|
||||
def self.group_lookup_columns
|
||||
|
|
|
@ -815,7 +815,9 @@ describe GroupsController do
|
|||
|
||||
put "/groups/#{group.id}.json", params: {
|
||||
group: {
|
||||
flair_bg_color: 'FFF',
|
||||
flair_color: 'BBB',
|
||||
flair_icon: 'fa-adjust',
|
||||
name: 'testing',
|
||||
visibility_level: 1,
|
||||
mentionable_level: 1,
|
||||
|
@ -829,7 +831,9 @@ describe GroupsController do
|
|||
expect(response.status).to eq(200)
|
||||
|
||||
group.reload
|
||||
expect(group.flair_color).to eq(nil)
|
||||
expect(group.flair_bg_color).to eq('FFF')
|
||||
expect(group.flair_color).to eq('BBB')
|
||||
expect(group.flair_icon).to eq('fa-adjust')
|
||||
expect(group.name).to eq('admins')
|
||||
expect(group.visibility_level).to eq(1)
|
||||
expect(group.mentionable_level).to eq(1)
|
||||
|
@ -916,6 +920,9 @@ describe GroupsController do
|
|||
|
||||
put "/groups/#{group.id}.json", params: {
|
||||
group: {
|
||||
flair_bg_color: 'FFF',
|
||||
flair_color: 'BBB',
|
||||
flair_icon: 'fa-adjust',
|
||||
mentionable_level: 1,
|
||||
messageable_level: 1,
|
||||
default_notification_level: 1
|
||||
|
@ -925,7 +932,9 @@ describe GroupsController do
|
|||
expect(response.status).to eq(200)
|
||||
|
||||
group.reload
|
||||
expect(group.flair_color).to eq(nil)
|
||||
expect(group.flair_bg_color).to eq('FFF')
|
||||
expect(group.flair_color).to eq('BBB')
|
||||
expect(group.flair_icon).to eq('fa-adjust')
|
||||
expect(group.name).to eq('trust_level_4')
|
||||
expect(group.mentionable_level).to eq(1)
|
||||
expect(group.messageable_level).to eq(1)
|
||||
|
|
Loading…
Reference in New Issue