+ ${topic.solvedByHtml}
+ <\/div>
+
+ ${topic.accepterHtml}
+ <\/div>
+
<\/div>
-
- ${topic.accepted_answer.excerpt}
-
+ ${excerpt}
`;
- const withoutExcerpt = `
-
`;
-
- const cooked = new PostCooked(
- { cooked: hasExcerpt ? withExcerpt : withoutExcerpt },
- dec
- );
+ const cooked = new PostCooked({ cooked: solvedQuote }, dec);
return dec.rawHtml(cooked.init());
}
}
@@ -67,7 +61,7 @@ function initializeWithApi(api) {
});
api.attachWidgetAction("post", "acceptAnswer", function () {
- acceptAnswer(this.model, this.appEvents);
+ acceptAnswer(this.model, this.appEvents, this.currentUser);
});
api.attachWidgetAction("post", "unacceptAnswer", function () {
@@ -173,7 +167,7 @@ export default {
initialize() {
Topic.reopen({
// keeping this here cause there is complex localization
- acceptedAnswerHtml: computed("accepted_answer", "id", function () {
+ 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");
@@ -196,6 +190,18 @@ export default {
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) => {
diff --git a/assets/stylesheets/solutions.scss b/assets/stylesheets/solutions.scss
index 40888e0..fba5370 100644
--- a/assets/stylesheets/solutions.scss
+++ b/assets/stylesheets/solutions.scss
@@ -62,8 +62,37 @@ $solved-color: green;
font-size: 13px;
}
-aside.quote .title.title-only {
- padding: 12px;
+aside.quote.accepted-answer {
+ .title {
+ display: flex;
+ flex-wrap: wrap;
+
+ &.title-only {
+ padding: 12px;
+ }
+ }
+
+ .accepted-answer--solver {
+ margin-right: auto;
+ }
+
+ .accepted-answer--accepter {
+ font-size: var(--font-down-1);
+ margin-left: auto;
+ margin-top: auto;
+ }
+
+ @media screen and (max-width: 768px) {
+ .accepted-answer--accepter {
+ width: 100%;
+ margin-top: 0.25em;
+ order: 3;
+ }
+
+ .quote-controls {
+ order: 2;
+ }
+ }
}
.user-card-metadata-outlet.accepted-answers {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 167b92a..f1c36b8 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -33,6 +33,7 @@ en:
no_solved_topics_title: "You haven’t solved any topics yet"
no_solved_topics_title_others: "%{username} has not solved any topics yet"
no_solved_topics_body: "When you provide a helpful reply to a topic, your reply might be selected as the solution by the topic owner or staff."
+ marked_solved_by: "Marked as solved by
%{username}"
no_answer:
title: Has your question been answered?
diff --git a/lib/discourse_solved/topic_view_serializer_extension.rb b/lib/discourse_solved/topic_view_serializer_extension.rb
index 22807fe..f0b1c2d 100644
--- a/lib/discourse_solved/topic_view_serializer_extension.rb
+++ b/lib/discourse_solved/topic_view_serializer_extension.rb
@@ -6,43 +6,46 @@ module DiscourseSolved::TopicViewSerializerExtension
prepended { attributes :accepted_answer }
def include_accepted_answer?
- SiteSetting.solved_enabled? && accepted_answer_post_id
+ SiteSetting.solved_enabled? && object.topic.solved.present?
end
def accepted_answer
- if info = accepted_answer_post_info
- { post_number: info[0], username: info[1], excerpt: info[2], name: info[3] }
- end
+ accepted_answer_post_info
end
private
def accepted_answer_post_info
- post_info =
- if post = object.posts.find { |p| p.post_number == accepted_answer_post_id }
- [post.post_number, post.user.username, post.cooked, post.user.name]
- else
- Post
- .where(id: accepted_answer_post_id, topic_id: object.topic.id)
- .joins(:user)
- .pluck("post_number", "username", "cooked", "name")
- .first
- end
+ solved = object.topic.solved
+ answer_post = solved.answer_post
+ answer_post_user = answer_post.user
+ accepter = solved.accepter
- if post_info
- post_info[2] = if SiteSetting.solved_quote_length > 0
- PrettyText.excerpt(post_info[2], SiteSetting.solved_quote_length, keep_emoji_images: true)
+ excerpt =
+ if SiteSetting.solved_quote_length > 0
+ PrettyText.excerpt(
+ answer_post.cooked,
+ SiteSetting.solved_quote_length,
+ keep_emoji_images: true,
+ )
else
nil
end
- post_info[3] = nil if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
+ accepted_answer = {
+ post_number: answer_post.post_number,
+ username: answer_post_user.username,
+ name: answer_post_user.name,
+ accepter_username: accepter.username,
+ accepter_name: accepter.name,
+ excerpt:,
+ }
- post_info
+ if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
+ accepted_answer[:name] = nil
+ accepted_answer[:accepter_name] = nil
end
- end
- def accepted_answer_post_id
- object.topic.solved&.answer_post_id
+ accepted_answer
end
end
diff --git a/spec/requests/topics_controller_spec.rb b/spec/requests/topics_controller_spec.rb
index 608f305..7d0633c 100644
--- a/spec/requests/topics_controller_spec.rb
+++ b/spec/requests/topics_controller_spec.rb
@@ -65,18 +65,21 @@ RSpec.describe TopicsController do
it "should include user name in output with the corresponding site setting" do
SiteSetting.display_name_on_posts = true
- Fabricate(:solved_topic, topic: topic, answer_post: p2)
+ accepter = Fabricate(:user)
+ Fabricate(:solved_topic, topic: topic, answer_post: p2, accepter:)
get "/t/#{topic.slug}/#{topic.id}.json"
expect(response.parsed_body["accepted_answer"]["name"]).to eq(p2.user.name)
expect(response.parsed_body["accepted_answer"]["username"]).to eq(p2.user.username)
+ expect(response.parsed_body["accepted_answer"]["accepter_name"]).to eq(accepter.name)
+ expect(response.parsed_body["accepted_answer"]["accepter_username"]).to eq(accepter.username)
# enable_names is default ON, this ensures disabling it also disables names here
SiteSetting.enable_names = false
get "/t/#{topic.slug}/#{topic.id}.json"
expect(response.parsed_body["accepted_answer"]["name"]).to eq(nil)
- expect(response.parsed_body["accepted_answer"]["username"]).to eq(p2.user.username)
+ expect(response.parsed_body["accepted_answer"]["accepter_name"]).to eq(nil)
end
it "should not include user name when site setting is disabled" do
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",
+ },
};
};