discourse-solved/assets/javascripts/discourse/routes/user-activity-solved.js
Natalie Tay f96aceb5f4
FIX: Use SolvedTopics to list posts in /activity/solved instead of user actions (#376)
In https://github.com/discourse/discourse-solved/pull/342 we moved solutions away from topic_custom_fields into proper tables, with the tables as the proper source of truth to a topic's solution.

The user's /my/activity/solved route uses user_actions which is not accurate, and a user has reported a bug where their solution is not reflected there (user actions are not a good representation of what a topic's solution is). 

This commit introduces 
- a new route to get solutions, and is mindful `hide_user_profiles_from_public` and such settings
- also mindful of PMs and private categories
- a new template that makes use of the `<UserStream>` to load posts safely and avoid reimplementation
2025-07-02 16:56:12 +08:00

119 lines
2.9 KiB
JavaScript

import { tracked } from "@glimmer/tracking";
import EmberObject from "@ember/object";
import { service } from "@ember/service";
import { Promise } from "rsvp";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
import { i18n } from "discourse-i18n";
class SolvedPostsStream {
@tracked content = [];
@tracked loading = false;
@tracked loaded = false;
@tracked itemsLoaded = 0;
@tracked canLoadMore = true;
constructor({ username, siteCategories }) {
this.username = username;
this.siteCategories = siteCategories;
}
get noContent() {
return this.loaded && this.content.length === 0;
}
findItems() {
if (this.loading || !this.canLoadMore) {
return Promise.resolve();
}
this.loading = true;
const limit = 20;
return ajax(
`/solution/by_user.json?username=${this.username}&offset=${this.itemsLoaded}&limit=${limit}`
)
.then((result) => {
const userSolvedPosts = result.user_solved_posts || [];
if (userSolvedPosts.length === 0) {
this.canLoadMore = false;
return;
}
const posts = userSolvedPosts.map((p) => {
const post = EmberObject.create(p);
post.set("titleHtml", post.topic_title);
post.set("postUrl", post.url);
if (post.category_id && this.siteCategories) {
post.set(
"category",
this.siteCategories.find((c) => c.id === post.category_id)
);
}
return post;
});
this.content = [...this.content, ...posts];
this.itemsLoaded = this.itemsLoaded + userSolvedPosts.length;
if (userSolvedPosts.length < limit) {
this.canLoadMore = false;
}
})
.finally(() => {
this.loaded = true;
this.loading = false;
});
}
}
export default class UserActivitySolved extends DiscourseRoute {
@service site;
@service currentUser;
model() {
const user = this.modelFor("user");
const stream = new SolvedPostsStream({
username: user.username,
siteCategories: this.site.categories,
});
return stream.findItems().then(() => {
return {
stream,
emptyState: this.emptyState(),
};
});
}
setupController(controller, model) {
controller.setProperties({
model,
emptyState: this.emptyState(),
});
}
renderTemplate() {
this.render("user-activity-solved");
}
emptyState() {
const user = this.modelFor("user");
let title, body;
if (this.currentUser && user.id === this.currentUser.id) {
title = i18n("solved.no_solved_topics_title");
body = i18n("solved.no_solved_topics_body");
} else {
title = i18n("solved.no_solved_topics_title_others", {
username: user.username,
});
body = "";
}
return { title, body };
}
}