diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js
index 827b901f4a7..49ffd0e37f3 100644
--- a/app/assets/javascripts/discourse/app/lib/transform-post.js
+++ b/app/assets/javascripts/discourse/app/lib/transform-post.js
@@ -289,6 +289,10 @@ export default function transformPost(
postAtts.isDeleted && post.can_permanently_delete;
}
+ if (post.user_status) {
+ postAtts.userStatus = post.user_status;
+ }
+
_additionalAttributes.forEach((a) => (postAtts[a] = post[a]));
return postAtts;
diff --git a/app/assets/javascripts/discourse/app/widgets/post-user-status.js b/app/assets/javascripts/discourse/app/widgets/post-user-status.js
new file mode 100644
index 00000000000..0e048575374
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/widgets/post-user-status.js
@@ -0,0 +1,18 @@
+import { createWidget } from "discourse/widgets/widget";
+import RenderGlimmer from "discourse/widgets/render-glimmer";
+import { hbs } from "ember-cli-htmlbars";
+
+createWidget("post-user-status", {
+ html(attrs) {
+ return [
+ new RenderGlimmer(
+ this,
+ "div",
+ hbs``,
+ {
+ attrs,
+ }
+ ),
+ ];
+ },
+});
diff --git a/app/assets/javascripts/discourse/app/widgets/poster-name.js b/app/assets/javascripts/discourse/app/widgets/poster-name.js
index 2308b77155a..50c1f392030 100644
--- a/app/assets/javascripts/discourse/app/widgets/poster-name.js
+++ b/app/assets/javascripts/discourse/app/widgets/poster-name.js
@@ -95,9 +95,6 @@ export default createWidget("poster-name", {
classNames.push("new-user");
}
- let afterNameContents =
- applyDecorators(this, "after-name", attrs, this.state) || [];
-
const primaryGroupName = attrs.primary_group_name;
if (primaryGroupName && primaryGroupName.length) {
classNames.push(primaryGroupName);
@@ -110,6 +107,8 @@ export default createWidget("poster-name", {
nameContents.push(glyph);
}
}
+
+ const afterNameContents = this.afterNameContents(attrs);
nameContents = nameContents.concat(afterNameContents);
const contents = [
@@ -149,4 +148,13 @@ export default createWidget("poster-name", {
return contents;
},
+
+ afterNameContents(attrs) {
+ const contents = [];
+ if (this.siteSettings.enable_user_status && attrs.userStatus) {
+ contents.push(this.attach("post-user-status", attrs.userStatus));
+ }
+ contents.push(...applyDecorators(this, "after-name", attrs, this.state));
+ return contents;
+ },
});
diff --git a/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js b/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
index b6ee555991c..08c1a81ab6c 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/widgets/post-test.js
@@ -917,4 +917,26 @@ module("Integration | Component | Widget | post", function (hooks) {
"/g/testGroup/requests?filter=foo"
);
});
+
+ test("shows user status if enabled in site settings", async function (assert) {
+ this.siteSettings.enable_user_status = true;
+ this.set("args", {
+ userStatus: { emoji: "tooth", description: "off to dentist" },
+ });
+
+ await render(hbs``);
+
+ assert.ok(exists(".user-status-message"));
+ });
+
+ test("doesn't show user status if disabled in site settings", async function (assert) {
+ this.siteSettings.enable_user_status = false;
+ this.set("args", {
+ userStatus: { emoji: "tooth", description: "off to dentist" },
+ });
+
+ await render(hbs``);
+
+ assert.notOk(exists(".user-status-message"));
+ });
});
diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss
index 4f0ea8faebd..5b664853090 100644
--- a/app/assets/stylesheets/common/base/topic-post.scss
+++ b/app/assets/stylesheets/common/base/topic-post.scss
@@ -74,6 +74,10 @@ $quote-share-maxwidth: 150px;
.user-title a {
color: var(--primary-med-or-secondary-med);
}
+
+ .user-status-message {
+ margin-left: 0.3em;
+ }
}
// global styles for the cooked HTML content in posts (and preview)
diff --git a/app/serializers/post_serializer.rb b/app/serializers/post_serializer.rb
index 472e3e87bb0..8f0bebf4c59 100644
--- a/app/serializers/post_serializer.rb
+++ b/app/serializers/post_serializer.rb
@@ -87,7 +87,8 @@ class PostSerializer < BasicPostSerializer
:reviewable_id,
:reviewable_score_count,
:reviewable_score_pending_count,
- :user_suspended
+ :user_suspended,
+ :user_status
def initialize(object, opts)
super(object, opts)
@@ -551,6 +552,14 @@ class PostSerializer < BasicPostSerializer
object.user&.suspended?
end
+ def include_user_status?
+ SiteSetting.enable_user_status && object.user&.has_status?
+ end
+
+ def user_status
+ UserStatusSerializer.new(object.user&.user_status, root: false)
+ end
+
private
def can_review_topic?
diff --git a/lib/topic_view.rb b/lib/topic_view.rb
index c3363715d60..1c2ed0e7b05 100644
--- a/lib/topic_view.rb
+++ b/lib/topic_view.rb
@@ -760,6 +760,8 @@ class TopicView
:image_upload
)
+ @posts = @posts.includes({ user: :user_status }) if SiteSetting.enable_user_status
+
@posts = apply_default_scope(@posts)
@posts = filter_post_types(@posts)
@posts = @posts.with_deleted if @guardian.can_see_deleted_posts?(@topic.category)
diff --git a/spec/serializers/post_serializer_spec.rb b/spec/serializers/post_serializer_spec.rb
index e47a83c104f..23c3123ad7a 100644
--- a/spec/serializers/post_serializer_spec.rb
+++ b/spec/serializers/post_serializer_spec.rb
@@ -299,6 +299,40 @@ RSpec.describe PostSerializer do
end
end
+ describe "#user_status" do
+ fab!(:user_status) { Fabricate(:user_status) }
+ fab!(:user) { Fabricate(:user, user_status: user_status) }
+ fab!(:post) { Fabricate(:post, user: user) }
+ let(:serializer) { described_class.new(post, scope: Guardian.new(user), root: false) }
+
+ it "adds user status when enabled" do
+ SiteSetting.enable_user_status = true
+
+ json = serializer.as_json
+
+ expect(json[:user_status]).to_not be_nil do |status|
+ expect(status.description).to eq(user_status.description)
+ expect(status.emoji).to eq(user_status.emoji)
+ end
+ end
+
+ it "doesn't add user status when disabled" do
+ SiteSetting.enable_user_status = false
+ json = serializer.as_json
+ expect(json.keys).not_to include :user_status
+ end
+
+ it "doesn't add status if user doesn't have it" do
+ SiteSetting.enable_user_status = true
+
+ user.clear_status!
+ user.reload
+ json = serializer.as_json
+
+ expect(json.keys).not_to include :user_status
+ end
+ end
+
def serialized_post(u)
s = PostSerializer.new(post, scope: Guardian.new(u), root: false)
s.add_raw = true