mirror of
https://github.com/discourse/discourse.git
synced 2025-02-20 23:19:25 +08:00
FIX: Don't lose SummaryBox state through widget re-renders. (#24020)
* FIX: Don't lose SummaryBox state through widget re-renders. The <SummaryBox /> component state will get lost when scrolling to the bottom of a topic. Due to the widget being re-rendered, it will go back to the collapsed state, and we need to fetch the summary again. This change moves all the state updates to the postStream model, which also refreshes the widget to keep it updated. * Reify topic summary using a pojo
This commit is contained in:
parent
0604dc7d3e
commit
a72e5fa763
@ -1,14 +1,10 @@
|
||||
<div
|
||||
class="summary-box__container"
|
||||
{{did-insert this.subscribe}}
|
||||
{{will-destroy this.unsubscribe}}
|
||||
>
|
||||
<div class="summary-box__container">
|
||||
{{#if @postAttrs.hasTopRepliesSummary}}
|
||||
<p>{{html-safe this.topRepliesSummaryInfo}}</p>
|
||||
{{/if}}
|
||||
<div class="summarization-buttons">
|
||||
{{#if @postAttrs.summarizable}}
|
||||
{{#if this.canCollapseSummary}}
|
||||
{{#if this.summary.showSummaryBox}}
|
||||
<DButton
|
||||
@action={{this.collapseSummary}}
|
||||
@title="summary.buttons.hide"
|
||||
@ -22,7 +18,7 @@
|
||||
@translatedLabel={{this.generateSummaryTitle}}
|
||||
@translatedTitle={{this.generateSummaryTitle}}
|
||||
@icon={{this.generateSummaryIcon}}
|
||||
@disabled={{this.loadingSummary}}
|
||||
@disabled={{this.summary.loading}}
|
||||
class="btn-primary topic-strategy-summarization"
|
||||
/>
|
||||
{{/if}}
|
||||
@ -39,29 +35,29 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if this.showSummaryBox}}
|
||||
{{#if this.summary.showSummaryBox}}
|
||||
<article class="summary-box">
|
||||
{{#if (and this.loadingSummary (not this.summary))}}
|
||||
{{#if (and this.summary.loading (not this.summary.text))}}
|
||||
<AiSummarySkeleton />
|
||||
{{else}}
|
||||
<div class="generated-summary">{{this.summary}}</div>
|
||||
<div class="generated-summary">{{this.summary.text}}</div>
|
||||
|
||||
{{#if this.summarizedOn}}
|
||||
{{#if this.summary.summarizedOn}}
|
||||
<div class="summarized-on">
|
||||
<p>
|
||||
{{i18n "summary.summarized_on" date=this.summarizedOn}}
|
||||
{{i18n "summary.summarized_on" date=this.summary.summarizedOn}}
|
||||
|
||||
<DTooltip @placements={{array "top-end"}}>
|
||||
<:trigger>
|
||||
{{d-icon "info-circle"}}
|
||||
</:trigger>
|
||||
<:content>
|
||||
{{i18n "summary.model_used" model=this.summarizedBy}}
|
||||
{{i18n "summary.model_used" model=this.summary.summarizedBy}}
|
||||
</:content>
|
||||
</DTooltip>
|
||||
</p>
|
||||
|
||||
{{#if this.outdated}}
|
||||
{{#if this.summary.outdated}}
|
||||
<p class="outdated-summary">
|
||||
{{this.outdatedSummaryWarningText}}
|
||||
</p>
|
||||
|
@ -1,69 +1,19 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { shortDateNoYear } from "discourse/lib/formatter";
|
||||
import { cook } from "discourse/lib/text";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
||||
const MIN_POST_READ_TIME = 4;
|
||||
|
||||
export default class SummaryBox extends Component {
|
||||
@service siteSettings;
|
||||
@service messageBus;
|
||||
@service currentUser;
|
||||
|
||||
@tracked summary = "";
|
||||
@tracked summarizedOn = null;
|
||||
@tracked summarizedBy = null;
|
||||
@tracked newPostsSinceSummary = null;
|
||||
@tracked outdated = false;
|
||||
@tracked canRegenerate = false;
|
||||
|
||||
@tracked regenerated = false;
|
||||
@tracked showSummaryBox = false;
|
||||
@tracked canCollapseSummary = false;
|
||||
@tracked loadingSummary = false;
|
||||
|
||||
@bind
|
||||
subscribe() {
|
||||
const channel = `/summaries/topic/${this.args.postAttrs.topicId}`;
|
||||
this.messageBus.subscribe(channel, this._updateSummary);
|
||||
}
|
||||
|
||||
@bind
|
||||
unsubscribe() {
|
||||
this.messageBus.unsubscribe("/summaries/topic/*", this._updateSummary);
|
||||
}
|
||||
|
||||
@bind
|
||||
_updateSummary(update) {
|
||||
const topicSummary = update.topic_summary;
|
||||
|
||||
if (topicSummary.summarized_text) {
|
||||
cook(topicSummary.summarized_text).then((cooked) => {
|
||||
this.summary = cooked;
|
||||
});
|
||||
}
|
||||
|
||||
if (update.done) {
|
||||
this.summarizedOn = shortDateNoYear(topicSummary.summarized_on);
|
||||
this.summarizedBy = topicSummary.algorithm;
|
||||
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
||||
this.outdated = topicSummary.outdated;
|
||||
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
||||
this.canRegenerate = topicSummary.outdated && topicSummary.can_regenerate;
|
||||
|
||||
this.canCollapseSummary = !this.canRegenerate;
|
||||
this.loadingSummary = false;
|
||||
}
|
||||
get summary() {
|
||||
return this.args.postAttrs.summary;
|
||||
}
|
||||
|
||||
get generateSummaryTitle() {
|
||||
const title = this.canRegenerate
|
||||
const title = this.summary.canRegenerate
|
||||
? "summary.buttons.regenerate"
|
||||
: "summary.buttons.generate";
|
||||
|
||||
@ -71,7 +21,7 @@ export default class SummaryBox extends Component {
|
||||
}
|
||||
|
||||
get generateSummaryIcon() {
|
||||
return this.canRegenerate ? "sync" : "magic";
|
||||
return this.summary.canRegenerate ? "sync" : "magic";
|
||||
}
|
||||
|
||||
get outdatedSummaryWarningText() {
|
||||
@ -79,11 +29,11 @@ export default class SummaryBox extends Component {
|
||||
|
||||
if (
|
||||
!this.args.postAttrs.hasTopRepliesSummary &&
|
||||
this.newPostsSinceSummary > 0
|
||||
this.summary.newPostsSinceSummary > 0
|
||||
) {
|
||||
outdatedText += " ";
|
||||
outdatedText += I18n.t("summary.outdated_posts", {
|
||||
count: this.newPostsSinceSummary,
|
||||
count: this.summary.newPostsSinceSummary,
|
||||
});
|
||||
}
|
||||
|
||||
@ -147,43 +97,16 @@ export default class SummaryBox extends Component {
|
||||
? "cancelFilter"
|
||||
: "showTopReplies";
|
||||
|
||||
this.args.topRepliesToggle(filterFunction);
|
||||
this.args.actionDispatchFunc(filterFunction);
|
||||
}
|
||||
|
||||
@action
|
||||
collapseSummary() {
|
||||
this.showSummaryBox = false;
|
||||
this.canCollapseSummary = false;
|
||||
this.args.actionDispatchFunc("collapseSummary");
|
||||
}
|
||||
|
||||
@action
|
||||
generateSummary() {
|
||||
this.showSummaryBox = true;
|
||||
|
||||
if (this.summary && !this.canRegenerate) {
|
||||
this.canCollapseSummary = true;
|
||||
return;
|
||||
} else {
|
||||
this.loadingSummary = true;
|
||||
}
|
||||
|
||||
let fetchURL = `/t/${this.args.postAttrs.topicId}/strategy-summary?`;
|
||||
|
||||
if (this.currentUser) {
|
||||
fetchURL += `stream=true`;
|
||||
|
||||
if (this.canRegenerate) {
|
||||
fetchURL += "&skip_age_check=true";
|
||||
}
|
||||
}
|
||||
|
||||
ajax(fetchURL)
|
||||
.then((data) => {
|
||||
if (!this.currentUser) {
|
||||
data.done = true;
|
||||
this._updateSummary(data);
|
||||
}
|
||||
})
|
||||
.catch(popupAjaxError);
|
||||
this.args.actionDispatchFunc("showSummary");
|
||||
}
|
||||
}
|
||||
|
@ -567,6 +567,14 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||
});
|
||||
},
|
||||
|
||||
collapseSummary() {
|
||||
this.get("model.postStream").collapseSummary();
|
||||
},
|
||||
|
||||
showSummary() {
|
||||
this.get("model.postStream").showSummary(this.currentUser);
|
||||
},
|
||||
|
||||
removeAllowedUser(user) {
|
||||
return this.get("model.details")
|
||||
.removeAllowedUser(user)
|
||||
@ -1617,6 +1625,9 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||
this.onMessage,
|
||||
this.get("model.message_bus_last_id")
|
||||
);
|
||||
|
||||
const summariesChannel = `/summaries/topic/${this.get("model.id")}`;
|
||||
this.messageBus.subscribe(summariesChannel, this._updateSummary);
|
||||
},
|
||||
|
||||
unsubscribe() {
|
||||
@ -1626,6 +1637,13 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||
}
|
||||
|
||||
this.messageBus.unsubscribe("/topic/*", this.onMessage);
|
||||
this.messageBus.unsubscribe("/summaries/topic/*", this._updateSummary);
|
||||
},
|
||||
|
||||
@bind
|
||||
_updateSummary(update) {
|
||||
const postStream = this.get("model.postStream");
|
||||
postStream.processSummaryUpdate(update);
|
||||
},
|
||||
|
||||
@bind
|
||||
|
72
app/assets/javascripts/discourse/app/lib/topic-summary.js
Normal file
72
app/assets/javascripts/discourse/app/lib/topic-summary.js
Normal file
@ -0,0 +1,72 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { shortDateNoYear } from "discourse/lib/formatter";
|
||||
import { cook } from "discourse/lib/text";
|
||||
|
||||
export default class TopicSummary {
|
||||
@tracked text = "";
|
||||
@tracked summarizedOn = null;
|
||||
@tracked summarizedBy = null;
|
||||
@tracked newPostsSinceSummary = null;
|
||||
@tracked outdated = false;
|
||||
@tracked canRegenerate = false;
|
||||
@tracked regenerated = false;
|
||||
|
||||
@tracked showSummaryBox = false;
|
||||
@tracked canCollapseSummary = false;
|
||||
@tracked loadingSummary = false;
|
||||
|
||||
processUpdate(update) {
|
||||
const topicSummary = update.topic_summary;
|
||||
|
||||
return cook(topicSummary.summarized_text)
|
||||
.then((cooked) => {
|
||||
this.text = cooked;
|
||||
this.loading = false;
|
||||
})
|
||||
.then(() => {
|
||||
if (update.done) {
|
||||
this.summarizedOn = shortDateNoYear(topicSummary.summarized_on);
|
||||
this.summarizedBy = topicSummary.algorithm;
|
||||
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
||||
this.outdated = topicSummary.outdated;
|
||||
this.newPostsSinceSummary = topicSummary.new_posts_since_summary;
|
||||
this.canRegenerate =
|
||||
topicSummary.outdated && topicSummary.can_regenerate;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
collapse() {
|
||||
this.showSummaryBox = false;
|
||||
this.canCollapseSummary = false;
|
||||
}
|
||||
|
||||
generateSummary(currentUser, topicId) {
|
||||
this.showSummaryBox = true;
|
||||
|
||||
if (this.text && !this.canRegenerate) {
|
||||
this.canCollapseSummary = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let fetchURL = `/t/${topicId}/strategy-summary?`;
|
||||
|
||||
if (currentUser) {
|
||||
fetchURL += `stream=true`;
|
||||
|
||||
if (this.canRegenerate) {
|
||||
fetchURL += "&skip_age_check=true";
|
||||
}
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
|
||||
return ajax(fetchURL).then((data) => {
|
||||
if (!currentUser) {
|
||||
data.done = true;
|
||||
this.processUpdate(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -220,6 +220,10 @@ export default function transformPost(
|
||||
postAtts.topicWordCount = topic.word_count;
|
||||
postAtts.hasTopRepliesSummary = topic.has_summary;
|
||||
postAtts.summarizable = topic.summarizable;
|
||||
|
||||
if (post.post_number === 1) {
|
||||
postAtts.summary = postStream.topicSummary;
|
||||
}
|
||||
}
|
||||
|
||||
if (postAtts.isDeleted) {
|
||||
|
@ -5,6 +5,7 @@ import { isEmpty } from "@ember/utils";
|
||||
import { Promise } from "rsvp";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import PostsWithPlaceholders from "discourse/lib/posts-with-placeholders";
|
||||
import TopicSummary from "discourse/lib/topic-summary";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { highlightPost } from "discourse/lib/utilities";
|
||||
import RestModel from "discourse/models/rest";
|
||||
@ -48,6 +49,7 @@ export default RestModel.extend({
|
||||
filterRepliesToPostNumber: null,
|
||||
filterUpwardsPostID: null,
|
||||
filter: null,
|
||||
topicSummary: null,
|
||||
|
||||
init() {
|
||||
this._identityMap = {};
|
||||
@ -71,6 +73,7 @@ export default RestModel.extend({
|
||||
loadingFilter: false,
|
||||
stagingPost: false,
|
||||
timelineLookup: [],
|
||||
topicSummary: new TopicSummary(),
|
||||
});
|
||||
},
|
||||
|
||||
@ -1260,6 +1263,18 @@ export default RestModel.extend({
|
||||
}
|
||||
},
|
||||
|
||||
collapseSummary() {
|
||||
this.topicSummary.collapse();
|
||||
},
|
||||
|
||||
showSummary(currentUser) {
|
||||
this.topicSummary.generateSummary(currentUser, this.get("topic.id"));
|
||||
},
|
||||
|
||||
processSummaryUpdate(update) {
|
||||
this.topicSummary.processUpdate(update);
|
||||
},
|
||||
|
||||
_initUserModels(post) {
|
||||
post.user = User.create({
|
||||
id: post.user_id,
|
||||
|
@ -359,6 +359,8 @@
|
||||
@toggleWiki={{action "toggleWiki"}}
|
||||
@showTopReplies={{action "showTopReplies"}}
|
||||
@cancelFilter={{action "cancelFilter"}}
|
||||
@collapseSummary={{action "collapseSummary"}}
|
||||
@showSummary={{action "showSummary"}}
|
||||
@removeAllowedUser={{action "removeAllowedUser"}}
|
||||
@removeAllowedGroup={{action "removeAllowedGroup"}}
|
||||
@topVisibleChanged={{action "topVisibleChanged"}}
|
||||
|
@ -408,7 +408,7 @@ export default createWidget("topic-map", {
|
||||
"section.information.toggle-summary",
|
||||
hbs`<SummaryBox
|
||||
@postAttrs={{@data.postAttrs}}
|
||||
@topRepliesToggle={{@data.actionDispatchFunc}}
|
||||
@actionDispatchFunc={{@data.actionDispatchFunc}}
|
||||
/>`,
|
||||
{
|
||||
postAttrs: attrs,
|
||||
|
Loading…
x
Reference in New Issue
Block a user