From 007980fdaf7a01d234070a2e0aa42b9647fed5da Mon Sep 17 00:00:00 2001 From: Andrei Prigorshnev Date: Mon, 29 Aug 2022 19:33:50 +0400 Subject: [PATCH] FEATURE: make user status on post streams live (#17903) --- .../discourse/app/lib/transform-post.js | 5 +- .../discourse/app/models/post-stream.js | 13 ++ .../discourse/app/widgets/poster-name.js | 22 ++- .../acceptance/topic-user-status-test.js | 127 ++++++++++++++++++ .../components/widgets/post-test.js | 19 ++- 5 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/discourse/tests/acceptance/topic-user-status-test.js diff --git a/app/assets/javascripts/discourse/app/lib/transform-post.js b/app/assets/javascripts/discourse/app/lib/transform-post.js index 37325972def..99e87733073 100644 --- a/app/assets/javascripts/discourse/app/lib/transform-post.js +++ b/app/assets/javascripts/discourse/app/lib/transform-post.js @@ -154,6 +154,7 @@ export default function transformPost( postAtts.topicUrl = topic.get("url"); postAtts.isSaving = post.isSaving; postAtts.staged = post.staged; + postAtts.user = post.user; if (post.notice) { postAtts.notice = post.notice; @@ -290,10 +291,6 @@ 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/models/post-stream.js b/app/assets/javascripts/discourse/app/models/post-stream.js index 040f77faa1e..af28c25b943 100644 --- a/app/assets/javascripts/discourse/app/models/post-stream.js +++ b/app/assets/javascripts/discourse/app/models/post-stream.js @@ -599,6 +599,7 @@ export default RestModel.extend({ }, prependPost(post) { + this._initUserModel(post); const stored = this.storePost(post); if (stored) { const posts = this.posts; @@ -609,6 +610,7 @@ export default RestModel.extend({ }, appendPost(post) { + this._initUserModel(post); const stored = this.storePost(post); if (stored) { const posts = this.posts; @@ -1245,6 +1247,17 @@ export default RestModel.extend({ } }, + _initUserModel(post) { + post.user = User.create({ + id: post.user_id, + username: post.username, + }); + + if (post.user_status) { + post.user.status = post.user_status; + } + }, + _checkIfShouldShowRevisions() { if (_lastEditNotificationClick) { const copy = _lastEditNotificationClick; diff --git a/app/assets/javascripts/discourse/app/widgets/poster-name.js b/app/assets/javascripts/discourse/app/widgets/poster-name.js index 50c1f392030..77a79fed308 100644 --- a/app/assets/javascripts/discourse/app/widgets/poster-name.js +++ b/app/assets/javascripts/discourse/app/widgets/poster-name.js @@ -42,6 +42,20 @@ export default createWidget("poster-name", { showGlyph: true, }, + didRenderWidget() { + if (this.attrs.user) { + this.attrs.user.trackStatus(); + this.attrs.user.on("status-changed", this, "scheduleRerender"); + } + }, + + willRerenderWidget() { + if (this.attrs.user) { + this.attrs.user.off("status-changed", this, "scheduleRerender"); + this.attrs.user.stopTrackingStatus(); + } + }, + // TODO: Allow extensibility posterGlyph(attrs) { if (attrs.moderator || attrs.groupModerator) { @@ -151,8 +165,12 @@ export default createWidget("poster-name", { afterNameContents(attrs) { const contents = []; - if (this.siteSettings.enable_user_status && attrs.userStatus) { - contents.push(this.attach("post-user-status", attrs.userStatus)); + if ( + this.siteSettings.enable_user_status && + attrs.user && + attrs.user.status + ) { + contents.push(this.attach("post-user-status", attrs.user.status)); } contents.push(...applyDecorators(this, "after-name", attrs, this.state)); return contents; diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-user-status-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-user-status-test.js new file mode 100644 index 00000000000..c8960b64fb2 --- /dev/null +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-user-status-test.js @@ -0,0 +1,127 @@ +import { + acceptance, + exists, + publishToMessageBus, + query, + queryAll, +} from "discourse/tests/helpers/qunit-helpers"; +import { visit } from "@ember/test-helpers"; +import { test } from "qunit"; +import TopicFixtures from "discourse/tests/fixtures/topic"; +import { cloneJSON } from "discourse-common/lib/object"; + +acceptance("Topic - User Status", function (needs) { + const status = { emoji: "tooth", description: "off to dentist" }; + + needs.user(); + needs.pretender((server, helper) => { + server.get("/t/299/1.json", () => { + const response = cloneJSON(TopicFixtures["/t/299/1.json"]); + response.post_stream.posts.forEach((post) => { + post.user_status = status; + }); + + return helper.response(200, response); + }); + }); + + test("shows user status next to avatar on posts", async function (assert) { + this.siteSettings.enable_user_status = true; + await visit("/t/-/299/1"); + + assert.equal( + queryAll(".topic-post .user-status-message").length, + 3, + "all posts has user status" + ); + }); +}); + +acceptance("Topic - User Status - live updates", function (needs) { + const userId = 1; + const status = { emoji: "tooth", description: "off to dentist" }; + + needs.user(); + needs.pretender((server, helper) => { + server.get("/t/299/1.json", () => { + const response = cloneJSON(TopicFixtures["/t/299/1.json"]); + response.post_stream.posts.forEach((post) => { + post.user_id = userId; + post.user_status = { emoji: "tooth", description: "off to dentist" }; + }); + + return helper.response(200, response); + }); + }); + + test("updating status", async function (assert) { + this.siteSettings.enable_user_status = true; + + await visit("/t/-/299/1"); + assert.equal( + queryAll(".topic-post .user-status-message").length, + 3, + "all posts has user status" + ); + assert.ok( + query(".topic-post .user-status-message .emoji").src.includes( + status.emoji + ), + "status emoji is correct" + ); + + const newStatus = { emoji: "surfing_man", description: "surfing" }; + await publishToMessageBus(`/user-status`, { [userId]: newStatus }); + + assert.equal( + queryAll(".topic-post .user-status-message").length, + 3, + "all posts has user status" + ); + assert.ok( + query(".topic-post .user-status-message .emoji").src.includes( + newStatus.emoji + ), + "status emoji is correct" + ); + }); + + test("removing status and setting again", async function (assert) { + this.siteSettings.enable_user_status = true; + + await visit("/t/-/299/1"); + assert.equal( + queryAll(".topic-post .user-status-message").length, + 3, + "all posts has user status" + ); + assert.ok( + query(".topic-post .user-status-message .emoji").src.includes( + status.emoji + ), + "status emoji is correct" + ); + + await publishToMessageBus(`/user-status`, { [userId]: null }); + + assert.notOk( + exists(".topic-post .user-status-message"), + "status on all posts has disappeared" + ); + + const newStatus = { emoji: "surfing_man", description: "surfing" }; + await publishToMessageBus(`/user-status`, { [userId]: newStatus }); + + assert.equal( + queryAll(".topic-post .user-status-message").length, + 3, + "all posts have user status" + ); + assert.ok( + query(".topic-post .user-status-message .emoji").src.includes( + newStatus.emoji + ), + "status emoji is correct" + ); + }); +}); 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 08c1a81ab6c..23226ec1a7e 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 @@ -11,6 +11,7 @@ import { hbs } from "ember-cli-htmlbars"; import EmberObject from "@ember/object"; import I18n from "I18n"; import createStore from "discourse/tests/helpers/create-store"; +import User from "discourse/models/user"; module("Integration | Component | Widget | post", function (hooks) { setupRenderingTest(hooks); @@ -920,9 +921,12 @@ module("Integration | Component | Widget | post", function (hooks) { 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" }, - }); + const status = { + emoji: "tooth", + description: "off to dentist", + }; + const user = User.create({ status }); + this.set("args", { user }); await render(hbs``); @@ -931,9 +935,12 @@ module("Integration | Component | Widget | post", function (hooks) { 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" }, - }); + const status = { + emoji: "tooth", + description: "off to dentist", + }; + const user = User.create({ status }); + this.set("args", { user }); await render(hbs``);