From 75cd3060a8086d4ff436ecc9c0d0ca1aa99c2233 Mon Sep 17 00:00:00 2001 From: Nat Date: Mon, 24 Mar 2025 20:15:57 +0800 Subject: [PATCH] Revert to widget instead of glimmer component --- .../solved-accept-answer-button.gjs | 10 +- .../discourse/components/solved-post.gjs | 100 ------------------ .../initializers/extend-for-solved-button.js | 80 +++++++++++++- spec/system/solved_spec.rb | 35 ++++++ .../helpers/discourse-solved-helpers.js | 8 +- .../integratin/discourse-solved-post-test.js | 89 ---------------- 6 files changed, 124 insertions(+), 198 deletions(-) delete mode 100644 assets/javascripts/discourse/components/solved-post.gjs create mode 100644 spec/system/solved_spec.rb delete mode 100644 test/javascripts/integratin/discourse-solved-post-test.js diff --git a/assets/javascripts/discourse/components/solved-accept-answer-button.gjs b/assets/javascripts/discourse/components/solved-accept-answer-button.gjs index aa885ba..6141336 100644 --- a/assets/javascripts/discourse/components/solved-accept-answer-button.gjs +++ b/assets/javascripts/discourse/components/solved-accept-answer-button.gjs @@ -19,7 +19,7 @@ export default class SolvedAcceptAnswerButton extends Component { @action acceptAnswer() { - acceptAnswer(this.args.post, this.appEvents); + acceptAnswer(this.args.post, this.appEvents, this.currentUser); } } -export function acceptAnswer(post, appEvents) { +export function acceptAnswer(post, appEvents, acceptingUser) { // TODO (glimmer-post-menu): Remove this exported function and move the code into the button action after the widget code is removed - acceptPost(post); + acceptPost(post, acceptingUser); appEvents.trigger("discourse-solved:solution-toggled", post); @@ -46,7 +46,7 @@ export function acceptAnswer(post, appEvents) { }); } -function acceptPost(post) { +function acceptPost(post, acceptingUser) { const topic = post.topic; clearAccepted(topic); @@ -62,6 +62,8 @@ function acceptPost(post) { name: post.name, post_number: post.post_number, excerpt: post.cooked, + accepter_username: acceptingUser.username, + accepter_name: acceptingUser.name, }); ajax("/solution/accept", { diff --git a/assets/javascripts/discourse/components/solved-post.gjs b/assets/javascripts/discourse/components/solved-post.gjs deleted file mode 100644 index 0ddd09c..0000000 --- a/assets/javascripts/discourse/components/solved-post.gjs +++ /dev/null @@ -1,100 +0,0 @@ -import Component from "@glimmer/component"; -import { service } from "@ember/service"; -import { htmlSafe } from "@ember/template"; -import { not } from "truth-helpers"; -import concatClass from "discourse/helpers/concat-class"; -import { iconHTML } from "discourse/lib/icon-library"; -import { formatUsername } from "discourse/lib/utilities"; -import User from "discourse/models/user"; -import { i18n } from "discourse-i18n"; - -export default class SolvedPost extends Component { - static shouldRender(args) { - return args.post?.post_number === 1 && args.post?.topic?.accepted_answer; - } - - @service siteSettings; - - get answerPostPath() { - return `${this.args.outletArgs.post.topic.url}/${this.answerPostNumber}`; - } - - get acceptedAnswer() { - return this.args.outletArgs.post.topic.accepted_answer; - } - - get answerPostNumber() { - return this.acceptedAnswer?.post_number; - } - - get topicId() { - return this.args.outletArgs.post.topic.id; - } - - get hasExcerpt() { - return !!this.solvedExcerpt; - } - - get solvedExcerpt() { - return this.acceptedAnswer?.excerpt; - } - - get username() { - return this.acceptedAnswer?.username; - } - - get displayedUser() { - const { name, username } = this.acceptedAnswer || {}; - return this.siteSettings.display_name_on_posts && name - ? name - : formatUsername(username); - } - - get title() { - return i18n("solved.accepted_html", { - icon: iconHTML("square-check", { class: "accepted" }), - username_lower: this.username?.toLowerCase(), - username: this.displayedUser, - post_path: this.answerPostPath, - post_number: this.answerPostNumber, - user_path: User.create({ username: this.username }).path, - }); - } - - get accepter() { - const accepterUsername = this.acceptedAnswer?.accepter_username; - const accepterName = this.acceptedAnswer?.accepter_name; - const formattedUsername = this.siteSettings.display_name_on_posts && accepterName - ? accepterName - : formatUsername(accepterUsername); - return i18n("solved.marked_solved_by", { - username: formattedUsername, - username_lower: accepterUsername.toLowerCase() - }); - } - - -} diff --git a/assets/javascripts/discourse/initializers/extend-for-solved-button.js b/assets/javascripts/discourse/initializers/extend-for-solved-button.js index e0d4a70..bb5eead 100644 --- a/assets/javascripts/discourse/initializers/extend-for-solved-button.js +++ b/assets/javascripts/discourse/initializers/extend-for-solved-button.js @@ -1,12 +1,16 @@ +import { computed } from "@ember/object"; import discourseComputed from "discourse/lib/decorators"; import { withSilencedDeprecations } from "discourse/lib/deprecated"; -import { iconNode } from "discourse/lib/icon-library"; +import { iconHTML, iconNode } from "discourse/lib/icon-library"; import { withPluginApi } from "discourse/lib/plugin-api"; +import { formatUsername } from "discourse/lib/utilities"; +import Topic from "discourse/models/topic"; +import User from "discourse/models/user"; +import PostCooked from "discourse/widgets/post-cooked"; import { i18n } from "discourse-i18n"; import SolvedAcceptAnswerButton, { acceptAnswer, } from "../components/solved-accept-answer-button"; -import SolvedPost from "../components/solved-post"; import SolvedUnacceptAnswerButton, { unacceptAnswer, } from "../components/solved-unaccept-answer-button"; @@ -25,10 +29,39 @@ function initializeWithApi(api) { api.addDiscoveryQueryParam("solved", { replace: true, refreshModel: true }); } - api.renderBeforeWrapperOutlet("post-menu", SolvedPost); + api.decorateWidget("post-contents:after-cooked", (dec) => { + if (dec.attrs.post_number === 1) { + const postModel = dec.getModel(); + if (postModel) { + const topic = postModel.topic; + if (topic.accepted_answer) { + const hasExcerpt = !!topic.accepted_answer.excerpt; + const excerpt = hasExcerpt + ? `
${topic.accepted_answer.excerpt}
` + : ""; + const solvedQuote = ` + `; + + const cooked = new PostCooked({ cooked: solvedQuote }, dec); + return dec.rawHtml(cooked.init()); + } + } + } + }); api.attachWidgetAction("post", "acceptAnswer", function () { - acceptAnswer(this.model, this.appEvents); + acceptAnswer(this.model, this.appEvents, this.currentUser); }); api.attachWidgetAction("post", "unacceptAnswer", function () { @@ -132,6 +165,45 @@ function customizeWidgetPostMenu(api) { export default { name: "extend-for-solved-button", initialize() { + Topic.reopen({ + // keeping this here cause there is complex localization + solvedByHtml: computed("accepted_answer", "id", function () { + const username = this.get("accepted_answer.username"); + const name = this.get("accepted_answer.name"); + const postNumber = this.get("accepted_answer.post_number"); + + if (!username || !postNumber) { + return ""; + } + + const displayedUser = + this.siteSettings.display_name_on_posts && name + ? name + : formatUsername(username); + + return i18n("solved.accepted_html", { + icon: iconHTML("square-check", { class: "accepted" }), + username_lower: username.toLowerCase(), + username: displayedUser, + post_path: `${this.url}/${postNumber}`, + post_number: postNumber, + user_path: User.create({ username }).path, + }); + }), + accepterHtml: computed("accepted_answer", function () { + const username = this.get("accepted_answer.accepter_username"); + const name = this.get("accepted_answer.accepter_name"); + const formattedUsername = + this.siteSettings.display_name_on_posts && name + ? name + : formatUsername(username); + return i18n("solved.marked_solved_by", { + username: formattedUsername, + username_lower: username.toLowerCase(), + }); + }), + }); + withPluginApi("2.0.0", (api) => { withSilencedDeprecations("discourse.hbr-topic-list-overrides", () => { let topicStatusIcons; diff --git a/spec/system/solved_spec.rb b/spec/system/solved_spec.rb new file mode 100644 index 0000000..ab736c7 --- /dev/null +++ b/spec/system/solved_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +describe "About page", type: :system do + fab!(:admin) + fab!(:solver) { Fabricate(:user) } + fab!(:accepter) { Fabricate(:user) } + fab!(:topic) { Fabricate(:post, user: admin).topic } + fab!(:post1) { Fabricate(:post, topic:, user: solver, cooked: "The answer is 42") } + let(:topic_page) { PageObjects::Pages::Topic.new } + + before do + SiteSetting.solved_enabled = true + SiteSetting.allow_solved_on_all_topics = true + SiteSetting.accept_all_solutions_allowed_groups = Group::AUTO_GROUPS[:everyone] + end + + it "accepts post as solution and shows in OP" do + sign_in(accepter) + + topic_page.visit_topic(topic, post_number: 2) + + expect(topic_page).to have_css(".post-action-menu__solved-unaccepted") + + find(".post-action-menu__solved-unaccepted").click + + expect(topic_page).to have_css(".post-action-menu__solved-accepted") + expect(topic_page.find(".title .accepted-answer--solver")).to have_content( + "Solved by #{solver.username}", + ) + expect(topic_page.find(".title .accepted-answer--accepter")).to have_content( + "Marked as solved by #{accepter.username}", + ) + expect(topic_page.find("blockquote")).to have_content("The answer is 42") + end +end diff --git a/test/javascripts/helpers/discourse-solved-helpers.js b/test/javascripts/helpers/discourse-solved-helpers.js index 2ee351e..6060b74 100644 --- a/test/javascripts/helpers/discourse-solved-helpers.js +++ b/test/javascripts/helpers/discourse-solved-helpers.js @@ -199,6 +199,12 @@ export const postStreamWithAcceptedAnswerExcerpt = (excerpt) => { featured_link: null, topic_timer: null, message_bus_last_id: 0, - accepted_answer: { post_number: 2, username: "kzh", excerpt }, + accepted_answer: { + post_number: 2, + username: "kzh", + excerpt, + accepter_username: "tomtom", + accepter_name: "Tomtom", + }, }; }; diff --git a/test/javascripts/integratin/discourse-solved-post-test.js b/test/javascripts/integratin/discourse-solved-post-test.js deleted file mode 100644 index 0c51703..0000000 --- a/test/javascripts/integratin/discourse-solved-post-test.js +++ /dev/null @@ -1,89 +0,0 @@ -import { render } from "@ember/test-helpers"; -import { hbs } from "ember-cli-htmlbars"; -import { module, test } from "qunit"; -import { setupRenderingTest } from "discourse/tests/helpers/component-test"; - -module("Integration | Component | solved-post", function (hooks) { - setupRenderingTest(hooks); - - test("renders solved post information", async function (assert) { - this.siteSettings = { - display_name_on_posts: true - }; - - const post = { - post_number: 1, - topic: { - id: 123, - url: "/t/topic/123", - accepted_answer: { - username: "solver", - name: "Solver Person", - post_number: 7, - excerpt: "This is the solution", - accepter_username: "accepter", - accepter_name: "Accepter Person" - } - } - }; - - this.set("outletArgs", { post }); - - await render(hbs``); - - assert.dom(".accepted-answer").exists("shows accepted answer section"); - assert.dom(".accepted-answer[data-post='7']").exists("has correct post number"); - assert.dom(".accepted-answer[data-topic='123']").exists("has correct topic id"); - assert.dom(".title .accepted-answer--solver").includesText("Solved by solver in post #7", "shows solver name"); - assert.dom(".title .accepted-answer--accepter").includesText("Marked as solved by accepter", "shows accepter name"); - assert.dom("blockquote").hasText("This is the solution", "shows excerpt"); - }); - - test("handles missing excerpt", async function (assert) { - const post = { - post_number: 1, - topic: { - id: 123, - accepted_answer: { - username: "solver", - post_number: 7, - accepter_username: "accepter" - } - } - }; - - this.set("outletArgs", { post }); - - await render(hbs``); - - assert.dom(".title").hasClass("title-only", "adds title-only class when no excerpt"); - assert.dom("blockquote").doesNotExist("doesn't show blockquote without excerpt"); - }); - - test("uses username when display_name_on_posts is false", async function (assert) { - this.siteSettings = { - display_name_on_posts: false - }; - - const post = { - post_number: 1, - topic: { - id: 123, - accepted_answer: { - username: "solver", - name: "Solver Person", - post_number: 7, - accepter_username: "accepter", - accepter_name: "Accepter Person" - } - } - }; - - this.set("outletArgs", { post }); - - await render(hbs``); - - assert.dom(".title .accepted-answer--solver").includesText("solver", "shows username"); - assert.dom(".title .accepted-answer--accepter").includesText("accepter", "shows accepter username"); - }); -});