mirror of
https://github.com/discourse/discourse-solved.git
synced 2025-07-05 13:22:11 +00:00
FEATURE: Real-time updates for accepted and unaccepted solutions
Introduce real-time message bus updates for accepted and unaccepted solutions, ensuring live synchronization across users. Key changes: - Publish solution acceptance/unacceptance updates via MessageBus. - Refactor `accepted_answer_post_info` and related logic for cleaner handling of accepted answer data. - Update both backend and frontend to support reactive updates when solutions are toggled. - Add loading states for Accept/Unaccept buttons to enhance UX during async operations.
This commit is contained in:
parent
436ee57801
commit
5aed0b41c5
@ -13,9 +13,9 @@ class DiscourseSolved::AnswerController < ::ApplicationController
|
|||||||
|
|
||||||
guardian.ensure_can_accept_answer!(topic, post)
|
guardian.ensure_can_accept_answer!(topic, post)
|
||||||
|
|
||||||
DiscourseSolved.accept_answer!(post, current_user, topic: topic)
|
accepted_answer = DiscourseSolved.accept_answer!(post, current_user, topic: topic)
|
||||||
|
|
||||||
render json: success_json
|
render_json_dump(accepted_answer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def unaccept
|
def unaccept
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import DButton from "discourse/components/d-button";
|
import DButton from "discourse/components/d-button";
|
||||||
@ -13,6 +14,8 @@ export default class SolvedAcceptAnswerButton extends Component {
|
|||||||
@service appEvents;
|
@service appEvents;
|
||||||
@service currentUser;
|
@service currentUser;
|
||||||
|
|
||||||
|
@tracked saving = false;
|
||||||
|
|
||||||
get showLabel() {
|
get showLabel() {
|
||||||
return this.currentUser?.id === this.args.post.topicCreatedById;
|
return this.currentUser?.id === this.args.post.topicCreatedById;
|
||||||
}
|
}
|
||||||
@ -21,13 +24,17 @@ export default class SolvedAcceptAnswerButton extends Component {
|
|||||||
acceptAnswer() {
|
acceptAnswer() {
|
||||||
const post = this.args.post;
|
const post = this.args.post;
|
||||||
|
|
||||||
acceptPost(post, this.currentUser);
|
this.saving = true;
|
||||||
|
try {
|
||||||
|
acceptPost(post, this.currentUser);
|
||||||
|
} finally {
|
||||||
|
this.saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
this.appEvents.trigger("discourse-solved:solution-toggled", post);
|
this.appEvents.trigger("discourse-solved:solution-toggled", post);
|
||||||
|
|
||||||
|
// TODO (glimmer-post-stream) the Glimmer Post Stream does not listen to this event
|
||||||
post.get("topic.postStream.posts").forEach((p) => {
|
post.get("topic.postStream.posts").forEach((p) => {
|
||||||
p.set("topic_accepted_answer", true);
|
|
||||||
// TODO (glimmer-post-stream) the Glimmer Post Stream does not listen to this event
|
|
||||||
this.appEvents.trigger("post-stream:refresh", { id: p.id });
|
this.appEvents.trigger("post-stream:refresh", { id: p.id });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -37,6 +44,7 @@ export default class SolvedAcceptAnswerButton extends Component {
|
|||||||
class="post-action-menu__solved-unaccepted unaccepted"
|
class="post-action-menu__solved-unaccepted unaccepted"
|
||||||
...attributes
|
...attributes
|
||||||
@action={{this.acceptAnswer}}
|
@action={{this.acceptAnswer}}
|
||||||
|
@disabled={{this.saving}}
|
||||||
@icon="far-square-check"
|
@icon="far-square-check"
|
||||||
@label={{if this.showLabel "solved.solution"}}
|
@label={{if this.showLabel "solved.solution"}}
|
||||||
@title="solved.accept_answer"
|
@title="solved.accept_answer"
|
||||||
@ -44,42 +52,21 @@ export default class SolvedAcceptAnswerButton extends Component {
|
|||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|
||||||
function acceptPost(post, acceptingUser) {
|
async function acceptPost(post) {
|
||||||
|
if (!post.can_accept_answer || post.accepted_answer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const topic = post.topic;
|
const topic = post.topic;
|
||||||
|
|
||||||
clearAccepted(topic);
|
try {
|
||||||
|
const acceptedAnswer = await ajax("/solution/accept", {
|
||||||
|
type: "POST",
|
||||||
|
data: { id: post.id },
|
||||||
|
});
|
||||||
|
|
||||||
post.setProperties({
|
topic.setAcceptedSolution(acceptedAnswer);
|
||||||
can_unaccept_answer: true,
|
} catch (e) {
|
||||||
can_accept_answer: false,
|
popupAjaxError(e);
|
||||||
accepted_answer: true,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
topic.set("accepted_answer", {
|
|
||||||
username: post.username,
|
|
||||||
name: post.name,
|
|
||||||
post_number: post.post_number,
|
|
||||||
excerpt: post.cooked,
|
|
||||||
accepter_username: acceptingUser.username,
|
|
||||||
accepter_name: acceptingUser.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
ajax("/solution/accept", {
|
|
||||||
type: "POST",
|
|
||||||
data: { id: post.id },
|
|
||||||
}).catch(popupAjaxError);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAccepted(topic) {
|
|
||||||
const posts = topic.get("postStream.posts");
|
|
||||||
posts.forEach((post) => {
|
|
||||||
if (post.get("post_number") > 1) {
|
|
||||||
post.setProperties({
|
|
||||||
accepted_answer: false,
|
|
||||||
can_accept_answer: true,
|
|
||||||
can_unaccept_answer: false,
|
|
||||||
topic_accepted_answer: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
import { tracked } from "@glimmer/tracking";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { service } from "@ember/service";
|
import { service } from "@ember/service";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
|
import { and, not } from "truth-helpers";
|
||||||
import DButton from "discourse/components/d-button";
|
import DButton from "discourse/components/d-button";
|
||||||
import icon from "discourse/helpers/d-icon";
|
import icon from "discourse/helpers/d-icon";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
@ -10,44 +12,11 @@ import { formatUsername } from "discourse/lib/utilities";
|
|||||||
import { i18n } from "discourse-i18n";
|
import { i18n } from "discourse-i18n";
|
||||||
import DTooltip from "float-kit/components/d-tooltip";
|
import DTooltip from "float-kit/components/d-tooltip";
|
||||||
|
|
||||||
function unacceptPost(post) {
|
|
||||||
if (!post.can_unaccept_answer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const topic = post.topic;
|
|
||||||
|
|
||||||
post.setProperties({
|
|
||||||
can_accept_answer: true,
|
|
||||||
can_unaccept_answer: false,
|
|
||||||
accepted_answer: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
topic.accepted_answer = undefined;
|
|
||||||
|
|
||||||
ajax("/solution/unaccept", {
|
|
||||||
type: "POST",
|
|
||||||
data: { id: post.id },
|
|
||||||
}).catch(popupAjaxError);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class SolvedUnacceptAnswerButton extends Component {
|
export default class SolvedUnacceptAnswerButton extends Component {
|
||||||
@service appEvents;
|
@service appEvents;
|
||||||
@service siteSettings;
|
@service siteSettings;
|
||||||
|
|
||||||
@action
|
@tracked saving = false;
|
||||||
unacceptAnswer() {
|
|
||||||
const post = this.args.post;
|
|
||||||
|
|
||||||
unacceptPost(post);
|
|
||||||
|
|
||||||
this.appEvents.trigger("discourse-solved:solution-toggled", post);
|
|
||||||
|
|
||||||
post.get("topic.postStream.posts").forEach((p) => {
|
|
||||||
p.set("topic_accepted_answer", false);
|
|
||||||
// TODO (glimmer-post-stream) the Glimmer Post Stream does not listen to this event
|
|
||||||
this.appEvents.trigger("post-stream:refresh", { id: p.id });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get solvedBy() {
|
get solvedBy() {
|
||||||
if (!this.siteSettings.show_who_marked_solved) {
|
if (!this.siteSettings.show_who_marked_solved) {
|
||||||
@ -68,9 +37,28 @@ export default class SolvedUnacceptAnswerButton extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
async unacceptAnswer() {
|
||||||
|
const post = this.args.post;
|
||||||
|
|
||||||
|
this.saving = true;
|
||||||
|
try {
|
||||||
|
await unacceptPost(post);
|
||||||
|
} finally {
|
||||||
|
this.saving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.appEvents.trigger("discourse-solved:solution-toggled", post);
|
||||||
|
|
||||||
|
// TODO (glimmer-post-stream) the Glimmer Post Stream does not listen to this event
|
||||||
|
post.get("topic.postStream.posts").forEach((p) => {
|
||||||
|
this.appEvents.trigger("post-stream:refresh", { id: p.id });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span class="extra-buttons">
|
<span class="extra-buttons">
|
||||||
{{#if @post.can_unaccept_answer}}
|
{{#if (and @post.can_accept_answer @post.accepted_answer)}}
|
||||||
{{#if this.solvedBy}}
|
{{#if this.solvedBy}}
|
||||||
<DTooltip @identifier="post-action-menu__solved-accepted-tooltip">
|
<DTooltip @identifier="post-action-menu__solved-accepted-tooltip">
|
||||||
<:trigger>
|
<:trigger>
|
||||||
@ -92,6 +80,7 @@ export default class SolvedUnacceptAnswerButton extends Component {
|
|||||||
class="post-action-menu__solved-accepted accepted fade-out"
|
class="post-action-menu__solved-accepted accepted fade-out"
|
||||||
...attributes
|
...attributes
|
||||||
@action={{this.unacceptAnswer}}
|
@action={{this.unacceptAnswer}}
|
||||||
|
@disabled={{this.saving}}
|
||||||
@icon="square-check"
|
@icon="square-check"
|
||||||
@label="solved.solution"
|
@label="solved.solution"
|
||||||
@title="solved.unaccept_answer"
|
@title="solved.unaccept_answer"
|
||||||
@ -111,3 +100,22 @@ export default class SolvedUnacceptAnswerButton extends Component {
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function unacceptPost(post) {
|
||||||
|
if (!post.can_accept_answer || !post.accepted_answer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const topic = post.topic;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ajax("/solution/unaccept", {
|
||||||
|
type: "POST",
|
||||||
|
data: { id: post.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
topic.setAcceptedSolution(undefined);
|
||||||
|
} catch (e) {
|
||||||
|
popupAjaxError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,19 +11,11 @@ import SolvedUnacceptAnswerButton from "../components/solved-unaccept-answer-but
|
|||||||
function initializeWithApi(api) {
|
function initializeWithApi(api) {
|
||||||
customizePost(api);
|
customizePost(api);
|
||||||
customizePostMenu(api);
|
customizePostMenu(api);
|
||||||
|
handleMessages(api);
|
||||||
|
|
||||||
if (api.addDiscoveryQueryParam) {
|
if (api.addDiscoveryQueryParam) {
|
||||||
api.addDiscoveryQueryParam("solved", { replace: true, refreshModel: true });
|
api.addDiscoveryQueryParam("solved", { replace: true, refreshModel: true });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function customizePost(api) {
|
|
||||||
api.addTrackedPostProperties(
|
|
||||||
"can_accept_answer",
|
|
||||||
"can_unaccept_answer",
|
|
||||||
"accepted_answer",
|
|
||||||
"topic_accepted_answer"
|
|
||||||
);
|
|
||||||
|
|
||||||
api.modifyClass(
|
api.modifyClass(
|
||||||
"model:topic",
|
"model:topic",
|
||||||
@ -31,8 +23,41 @@ function customizePost(api) {
|
|||||||
class extends Superclass {
|
class extends Superclass {
|
||||||
@tracked accepted_answer;
|
@tracked accepted_answer;
|
||||||
@tracked has_accepted_answer;
|
@tracked has_accepted_answer;
|
||||||
|
|
||||||
|
setAcceptedSolution(acceptedAnswer) {
|
||||||
|
this.postStream?.posts?.forEach((post) => {
|
||||||
|
if (!acceptedAnswer) {
|
||||||
|
post.setProperties({
|
||||||
|
accepted_answer: false,
|
||||||
|
topic_accepted_answer: false,
|
||||||
|
});
|
||||||
|
} else if (post.post_number > 1) {
|
||||||
|
post.setProperties(
|
||||||
|
acceptedAnswer.post_number === post.post_number
|
||||||
|
? {
|
||||||
|
accepted_answer: true,
|
||||||
|
topic_accepted_answer: true,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
accepted_answer: false,
|
||||||
|
topic_accepted_answer: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.accepted_answer = acceptedAnswer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function customizePost(api) {
|
||||||
|
api.addTrackedPostProperties(
|
||||||
|
"can_accept_answer",
|
||||||
|
"accepted_answer",
|
||||||
|
"topic_accepted_answer"
|
||||||
|
);
|
||||||
|
|
||||||
api.renderAfterWrapperOutlet(
|
api.renderAfterWrapperOutlet(
|
||||||
"post-content-cooked-html",
|
"post-content-cooked-html",
|
||||||
@ -84,10 +109,10 @@ function customizePostMenu(api) {
|
|||||||
}) => {
|
}) => {
|
||||||
let solvedButton;
|
let solvedButton;
|
||||||
|
|
||||||
if (post.can_accept_answer) {
|
if (post.accepted_answer) {
|
||||||
solvedButton = SolvedAcceptAnswerButton;
|
|
||||||
} else if (post.accepted_answer) {
|
|
||||||
solvedButton = SolvedUnacceptAnswerButton;
|
solvedButton = SolvedUnacceptAnswerButton;
|
||||||
|
} else if (post.can_accept_answer) {
|
||||||
|
solvedButton = SolvedAcceptAnswerButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
solvedButton &&
|
solvedButton &&
|
||||||
@ -110,19 +135,32 @@ function customizePostMenu(api) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleMessages(api) {
|
||||||
|
const handleMessages = async (controller, message) => {
|
||||||
|
const topic = controller.model;
|
||||||
|
|
||||||
|
if (topic) {
|
||||||
|
topic.setAcceptedSolution(message.accepted_answer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.registerCustomPostMessageCallback("accepted_solution", handleMessages);
|
||||||
|
api.registerCustomPostMessageCallback("unaccepted_solution", handleMessages);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "extend-for-solved-button",
|
name: "extend-for-solved-button",
|
||||||
initialize() {
|
initialize() {
|
||||||
withPluginApi("1.34.0", initializeWithApi);
|
withPluginApi(initializeWithApi);
|
||||||
|
|
||||||
withPluginApi("0.8.10", (api) => {
|
withPluginApi((api) => {
|
||||||
api.replaceIcon(
|
api.replaceIcon(
|
||||||
"notification.solved.accepted_notification",
|
"notification.solved.accepted_notification",
|
||||||
"square-check"
|
"square-check"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
withPluginApi("0.11.0", (api) => {
|
withPluginApi((api) => {
|
||||||
api.addAdvancedSearchOptions({
|
api.addAdvancedSearchOptions({
|
||||||
statusOptions: [
|
statusOptions: [
|
||||||
{
|
{
|
||||||
@ -137,7 +175,7 @@ export default {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
withPluginApi("0.11.7", (api) => {
|
withPluginApi((api) => {
|
||||||
api.addSearchSuggestion("status:solved");
|
api.addSearchSuggestion("status:solved");
|
||||||
api.addSearchSuggestion("status:unsolved");
|
api.addSearchSuggestion("status:unsolved");
|
||||||
});
|
});
|
||||||
|
@ -21,7 +21,9 @@ module DiscourseSolved
|
|||||||
|
|
||||||
def can_accept_answer?(topic, post)
|
def can_accept_answer?(topic, post)
|
||||||
return false if !authenticated?
|
return false if !authenticated?
|
||||||
return false if !topic || topic.private_message? || !post || post.whisper?
|
if !topic || topic.private_message? || !post || post.post_number <= 1 || post.whisper?
|
||||||
|
return false
|
||||||
|
end
|
||||||
return false if !allow_accepted_answers?(topic.category_id, topic.tags.map(&:name))
|
return false if !allow_accepted_answers?(topic.category_id, topic.tags.map(&:name))
|
||||||
|
|
||||||
return true if is_staff?
|
return true if is_staff?
|
||||||
|
@ -4,4 +4,43 @@ module DiscourseSolved::TopicExtension
|
|||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
prepended { has_one :solved, class_name: "DiscourseSolved::SolvedTopic", dependent: :destroy }
|
prepended { has_one :solved, class_name: "DiscourseSolved::SolvedTopic", dependent: :destroy }
|
||||||
|
|
||||||
|
def accepted_answer_post_info
|
||||||
|
return nil unless solved
|
||||||
|
|
||||||
|
answer_post = solved.answer_post
|
||||||
|
|
||||||
|
answer_post_user = answer_post.user
|
||||||
|
accepter = solved.accepter
|
||||||
|
|
||||||
|
excerpt =
|
||||||
|
if SiteSetting.solved_quote_length > 0
|
||||||
|
PrettyText.excerpt(
|
||||||
|
answer_post.cooked,
|
||||||
|
SiteSetting.solved_quote_length,
|
||||||
|
keep_emoji_images: true,
|
||||||
|
)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
accepted_answer = {
|
||||||
|
post_number: answer_post.post_number,
|
||||||
|
username: answer_post_user.username,
|
||||||
|
name: answer_post_user.name,
|
||||||
|
excerpt:,
|
||||||
|
}
|
||||||
|
|
||||||
|
if SiteSetting.show_who_marked_solved
|
||||||
|
accepted_answer[:accepter_name] = accepter.name
|
||||||
|
accepted_answer[:accepter_username] = accepter.username
|
||||||
|
end
|
||||||
|
|
||||||
|
if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
|
||||||
|
accepted_answer[:name] = nil
|
||||||
|
accepted_answer[:accepter_name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
accepted_answer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -11,45 +11,6 @@ module DiscourseSolved::TopicViewSerializerExtension
|
|||||||
end
|
end
|
||||||
|
|
||||||
def accepted_answer
|
def accepted_answer
|
||||||
accepted_answer_post_info
|
object.topic.accepted_answer_post_info
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def accepted_answer_post_info
|
|
||||||
solved = object.topic.solved
|
|
||||||
answer_post = solved.answer_post
|
|
||||||
answer_post_user = answer_post.user
|
|
||||||
accepter = solved.accepter
|
|
||||||
|
|
||||||
excerpt =
|
|
||||||
if SiteSetting.solved_quote_length > 0
|
|
||||||
PrettyText.excerpt(
|
|
||||||
answer_post.cooked,
|
|
||||||
SiteSetting.solved_quote_length,
|
|
||||||
keep_emoji_images: true,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
accepted_answer = {
|
|
||||||
post_number: answer_post.post_number,
|
|
||||||
username: answer_post_user.username,
|
|
||||||
name: answer_post_user.name,
|
|
||||||
excerpt:,
|
|
||||||
}
|
|
||||||
|
|
||||||
if SiteSetting.show_who_marked_solved
|
|
||||||
accepted_answer[:accepter_name] = accepter.name
|
|
||||||
accepted_answer[:accepter_username] = accepter.username
|
|
||||||
end
|
|
||||||
|
|
||||||
if !SiteSetting.enable_names || !SiteSetting.display_name_on_posts
|
|
||||||
accepted_answer[:name] = nil
|
|
||||||
accepted_answer[:accepter_name] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
accepted_answer
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
13
plugin.rb
13
plugin.rb
@ -112,7 +112,14 @@ after_initialize do
|
|||||||
WebHook.enqueue_solved_hooks(:accepted_solution, post, payload)
|
WebHook.enqueue_solved_hooks(:accepted_solution, post, payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
accepted_answer = topic.reload.accepted_answer_post_info
|
||||||
|
|
||||||
|
message = { type: :accepted_solution, accepted_answer: }
|
||||||
|
|
||||||
DiscourseEvent.trigger(:accepted_solution, post)
|
DiscourseEvent.trigger(:accepted_solution, post)
|
||||||
|
MessageBus.publish("/topic/#{topic.id}", message)
|
||||||
|
|
||||||
|
accepted_answer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -140,7 +147,9 @@ after_initialize do
|
|||||||
payload = WebHook.generate_payload(:post, post)
|
payload = WebHook.generate_payload(:post, post)
|
||||||
WebHook.enqueue_solved_hooks(:unaccepted_solution, post, payload)
|
WebHook.enqueue_solved_hooks(:unaccepted_solution, post, payload)
|
||||||
end
|
end
|
||||||
|
|
||||||
DiscourseEvent.trigger(:unaccepted_solution, post)
|
DiscourseEvent.trigger(:unaccepted_solution, post)
|
||||||
|
MessageBus.publish("/topic/#{topic.id}", type: :unaccepted_solution)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -252,9 +261,7 @@ after_initialize do
|
|||||||
.count
|
.count
|
||||||
end
|
end
|
||||||
add_to_serializer(:user_summary, :solved_count) { object.solved_count }
|
add_to_serializer(:user_summary, :solved_count) { object.solved_count }
|
||||||
add_to_serializer(:post, :can_accept_answer) do
|
add_to_serializer(:post, :can_accept_answer) { scope.can_accept_answer?(topic, object) }
|
||||||
scope.can_accept_answer?(topic, object) && object.post_number > 1 && !accepted_answer
|
|
||||||
end
|
|
||||||
add_to_serializer(:post, :can_unaccept_answer) do
|
add_to_serializer(:post, :can_unaccept_answer) do
|
||||||
scope.can_accept_answer?(topic, object) && accepted_answer
|
scope.can_accept_answer?(topic, object) && accepted_answer
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user