DEV: Update topic and post models to native class syntax (#25612)

This commit was created with a combination of the ember-native-class-codemod and manual cleanup
This commit is contained in:
David Taylor 2024-02-08 15:09:50 +00:00 committed by GitHub
parent 6c597b648b
commit a3ef9e92eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 449 additions and 463 deletions

View File

@ -18,8 +18,99 @@ import User from "discourse/models/user";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
const Post = RestModel.extend({
customShare: null,
export default class Post extends RestModel {
static munge(json) {
if (json.actions_summary) {
const lookup = EmberObject.create();
// this area should be optimized, it is creating way too many objects per post
json.actions_summary = json.actions_summary.map((a) => {
a.actionType = Site.current().postActionTypeById(a.id);
a.count = a.count || 0;
const actionSummary = ActionSummary.create(a);
lookup[a.actionType.name_key] = actionSummary;
if (a.actionType.name_key === "like") {
json.likeAction = actionSummary;
}
return actionSummary;
});
json.actionByName = lookup;
}
if (json && json.reply_to_user) {
json.reply_to_user = User.create(json.reply_to_user);
}
return json;
}
static updateBookmark(postId, bookmarked) {
return ajax(`/posts/${postId}/bookmark`, {
type: "PUT",
data: { bookmarked },
});
}
static destroyBookmark(postId) {
return ajax(`/posts/${postId}/bookmark`, {
type: "DELETE",
});
}
static deleteMany(post_ids, { agreeWithFirstReplyFlag = true } = {}) {
return ajax("/posts/destroy_many", {
type: "DELETE",
data: { post_ids, agree_with_first_reply_flag: agreeWithFirstReplyFlag },
});
}
static mergePosts(post_ids) {
return ajax("/posts/merge_posts", {
type: "PUT",
data: { post_ids },
}).catch(popupAjaxError);
}
static loadRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}.json`).then((result) =>
EmberObject.create(result)
);
}
static hideRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}/hide`, {
type: "PUT",
});
}
static permanentlyDeleteRevisions(postId) {
return ajax(`/posts/${postId}/revisions/permanently_delete`, {
type: "DELETE",
});
}
static showRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}/show`, {
type: "PUT",
});
}
static loadRawEmail(postId) {
return ajax(`/posts/${postId}/raw-email.json`);
}
customShare = null;
@equal("trust_level", 0) new_user;
@equal("post_number", 1) firstPost;
@or("deleted_at", "deletedViaTopic") deleted;
@not("deleted") notDeleted;
@propertyEqual("topic.details.created_by.id", "user_id") topicOwner;
// Posts can show up as deleted if the topic is deleted
@and("firstPost", "topic.deleted_at") deletedViaTopic;
@discourseComputed("url", "customShare")
shareUrl(url) {
@ -29,30 +120,22 @@ const Post = RestModel.extend({
const user = User.current();
return resolveShareUrl(url, user);
},
new_user: equal("trust_level", 0),
firstPost: equal("post_number", 1),
// Posts can show up as deleted if the topic is deleted
deletedViaTopic: and("firstPost", "topic.deleted_at"),
deleted: or("deleted_at", "deletedViaTopic"),
notDeleted: not("deleted"),
}
@discourseComputed("name", "username")
showName(name, username) {
return name && name !== username && this.siteSettings.display_name_on_posts;
},
}
@discourseComputed("firstPost", "deleted_by", "topic.deleted_by")
postDeletedBy(firstPost, deletedBy, topicDeletedBy) {
return firstPost ? topicDeletedBy : deletedBy;
},
}
@discourseComputed("firstPost", "deleted_at", "topic.deleted_at")
postDeletedAt(firstPost, deletedAt, topicDeletedAt) {
return firstPost ? topicDeletedAt : deletedAt;
},
}
@discourseComputed("post_number", "topic_id", "topic.slug")
url(post_number, topic_id, topicSlug) {
@ -61,18 +144,18 @@ const Post = RestModel.extend({
topic_id || this.get("topic.id"),
post_number
);
},
}
// Don't drop the /1
@discourseComputed("post_number", "url")
urlWithNumber(postNumber, baseUrl) {
return postNumber === 1 ? `${baseUrl}/1` : baseUrl;
},
}
@discourseComputed("username")
usernameUrl: userPath,
topicOwner: propertyEqual("topic.details.created_by.id", "user_id"),
usernameUrl(username) {
return userPath(username);
}
updatePostField(field, value) {
const data = {};
@ -81,7 +164,7 @@ const Post = RestModel.extend({
return ajax(`/posts/${this.id}/${field}`, { type: "PUT", data })
.then(() => this.set(field, value))
.catch(popupAjaxError);
},
}
@discourseComputed("link_counts.@each.internal")
internalLinks() {
@ -90,7 +173,7 @@ const Post = RestModel.extend({
}
return this.link_counts.filterBy("internal").filterBy("title");
},
}
@discourseComputed("actions_summary.@each.can_act")
flagsAvailable() {
@ -103,7 +186,7 @@ const Post = RestModel.extend({
return this.site.flagTypes.filter((item) =>
this.get(`actionByName.${item.name_key}.can_act`)
);
},
}
@discourseComputed(
"siteSettings.use_pg_headlines_for_excerpt",
@ -111,25 +194,25 @@ const Post = RestModel.extend({
)
useTopicTitleHeadline(enabled, title) {
return enabled && title;
},
}
@discourseComputed("topic_title_headline")
topicTitleHeadline(title) {
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
},
}
afterUpdate(res) {
if (res.category) {
this.site.updateCategory(res.category);
}
},
}
updateProperties() {
return {
post: { raw: this.raw, edit_reason: this.editReason },
image_sizes: this.imageSizes,
};
},
}
createProperties() {
// composer only used once, defer the dependency
@ -148,7 +231,7 @@ const Post = RestModel.extend({
}
return data;
},
}
// Expands the first post's content, if embedded and shortened.
expand() {
@ -158,7 +241,7 @@ const Post = RestModel.extend({
`<section class="expanded-embed">${post.cooked}</section>`
);
});
},
}
// Recover a deleted post
recover() {
@ -192,7 +275,7 @@ const Post = RestModel.extend({
popupAjaxError(error);
this.setProperties(initProperties);
});
},
}
/**
Changes the state of the post to be deleted. Does not call the server, that should be
@ -231,7 +314,7 @@ const Post = RestModel.extend({
}
return promise || Promise.resolve();
},
}
/**
Changes the state of the post to NOT be deleted. Does not call the server.
@ -250,7 +333,7 @@ const Post = RestModel.extend({
user_deleted: false,
});
}
},
}
destroy(deletedBy, opts) {
return this.setDeletedState(deletedBy).then(() => {
@ -259,7 +342,7 @@ const Post = RestModel.extend({
type: "DELETE",
});
});
},
}
/**
Updates a post from another's attributes. This will normally happen when a post is loading but
@ -291,23 +374,23 @@ const Post = RestModel.extend({
}
}
});
},
}
expandHidden() {
return ajax(`/posts/${this.id}/cooked.json`).then((result) => {
this.setProperties({ cooked: result.cooked, cooked_hidden: false });
});
},
}
rebake() {
return ajax(`/posts/${this.id}/rebake`, { type: "PUT" }).catch(
popupAjaxError
);
},
}
unhide() {
return ajax(`/posts/${this.id}/unhide`, { type: "PUT" });
},
}
createBookmark(data) {
this.setProperties({
@ -324,12 +407,12 @@ const Post = RestModel.extend({
targetId: this.id,
});
this.appEvents.trigger("post-stream:refresh", { id: this.id });
},
}
deleteBookmark(bookmarked) {
this.set("topic.bookmarked", bookmarked);
this.clearBookmark();
},
}
clearBookmark() {
this.setProperties({
@ -344,14 +427,14 @@ const Post = RestModel.extend({
target: "post",
targetId: this.id,
});
},
}
updateActionsSummary(json) {
if (json && json.id === this.id) {
json = Post.munge(json);
this.set("actions_summary", json.actions_summary);
}
},
}
updateLikeCount(count, userId, eventType) {
let ownAction = User.current()?.id === userId;
@ -385,101 +468,15 @@ const Post = RestModel.extend({
Object.assign(this.actionByName["like"], newActionObject);
Object.assign(this.likeAction, newActionObject);
}
},
}
revertToRevision(version) {
return ajax(`/posts/${this.id}/revisions/${version}/revert`, {
type: "PUT",
});
},
}
get topicNotificationLevel() {
return this.topic.details.notification_level;
},
});
Post.reopenClass({
munge(json) {
if (json.actions_summary) {
const lookup = EmberObject.create();
// this area should be optimized, it is creating way too many objects per post
json.actions_summary = json.actions_summary.map((a) => {
a.actionType = Site.current().postActionTypeById(a.id);
a.count = a.count || 0;
const actionSummary = ActionSummary.create(a);
lookup[a.actionType.name_key] = actionSummary;
if (a.actionType.name_key === "like") {
json.likeAction = actionSummary;
}
return actionSummary;
});
json.actionByName = lookup;
}
if (json && json.reply_to_user) {
json.reply_to_user = User.create(json.reply_to_user);
}
return json;
},
updateBookmark(postId, bookmarked) {
return ajax(`/posts/${postId}/bookmark`, {
type: "PUT",
data: { bookmarked },
});
},
destroyBookmark(postId) {
return ajax(`/posts/${postId}/bookmark`, {
type: "DELETE",
});
},
deleteMany(post_ids, { agreeWithFirstReplyFlag = true } = {}) {
return ajax("/posts/destroy_many", {
type: "DELETE",
data: { post_ids, agree_with_first_reply_flag: agreeWithFirstReplyFlag },
});
},
mergePosts(post_ids) {
return ajax("/posts/merge_posts", {
type: "PUT",
data: { post_ids },
}).catch(popupAjaxError);
},
loadRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}.json`).then((result) =>
EmberObject.create(result)
);
},
hideRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}/hide`, {
type: "PUT",
});
},
permanentlyDeleteRevisions(postId) {
return ajax(`/posts/${postId}/revisions/permanently_delete`, {
type: "DELETE",
});
},
showRevision(postId, version) {
return ajax(`/posts/${postId}/revisions/${version}/show`, {
type: "PUT",
});
},
loadRawEmail(postId) {
return ajax(`/posts/${postId}/raw-email.json`);
},
});
export default Post;

View File

@ -45,20 +45,277 @@ export function loadTopicView(topic, args) {
export const ID_CONSTRAINT = /^\d+$/;
let _customLastUnreadUrlCallbacks = [];
const Topic = RestModel.extend({
message: null,
errorLoading: false,
export default class Topic extends RestModel {
static NotificationLevel = {
WATCHING: 3,
TRACKING: 2,
REGULAR: 1,
MUTED: 0,
};
static munge(json) {
// ensure we are not overriding category computed property
delete json.category;
json.bookmarks = json.bookmarks || [];
return json;
}
static createActionSummary(result) {
if (result.actions_summary) {
const lookup = EmberObject.create();
result.actions_summary = result.actions_summary.map((a) => {
a.post = result;
a.actionType = Site.current().postActionTypeById(a.id);
const actionSummary = ActionSummary.create(a);
lookup.set(a.actionType.get("name_key"), actionSummary);
return actionSummary;
});
result.set("actionByName", lookup);
}
}
static update(topic, props, opts = {}) {
// We support `category_id` and `categoryId` for compatibility
if (typeof props.categoryId !== "undefined") {
props.category_id = props.categoryId;
delete props.categoryId;
}
// Make sure we never change the category for private messages
if (topic.get("isPrivateMessage")) {
delete props.category_id;
}
const data = { ...props };
if (opts.fastEdit) {
data.keep_existing_draft = true;
}
return ajax(topic.get("url"), {
type: "PUT",
data: JSON.stringify(data),
contentType: "application/json",
}).then((result) => {
// The title can be cleaned up server side
props.title = result.basic_topic.title;
props.fancy_title = result.basic_topic.fancy_title;
if (topic.is_shared_draft) {
props.destination_category_id = props.category_id;
delete props.category_id;
}
topic.setProperties(props);
});
}
static create() {
const result = super.create.apply(this, arguments);
this.createActionSummary(result);
return result;
}
// Load a topic, but accepts a set of filters
static find(topicId, opts) {
let url = getURL("/t/") + topicId;
if (opts.nearPost) {
url += `/${opts.nearPost}`;
}
const data = {};
if (opts.postsAfter) {
data.posts_after = opts.postsAfter;
}
if (opts.postsBefore) {
data.posts_before = opts.postsBefore;
}
if (opts.trackVisit) {
data.track_visit = true;
}
// Add username filters if we have them
if (opts.userFilters && opts.userFilters.length > 0) {
data.username_filters = [];
opts.userFilters.forEach(function (username) {
data.username_filters.push(username);
});
}
// Add the summary of filter if we have it
if (opts.summary === true) {
data.summary = true;
}
// Check the preload store. If not, load it via JSON
return ajax(`${url}.json`, { data });
}
static changeOwners(topicId, opts) {
const promise = ajax(`/t/${topicId}/change-owner`, {
type: "POST",
data: opts,
}).then((result) => {
if (result.success) {
return result;
}
promise.reject(new Error("error changing ownership of posts"));
});
return promise;
}
static changeTimestamp(topicId, timestamp) {
const promise = ajax(`/t/${topicId}/change-timestamp`, {
type: "PUT",
data: { timestamp },
}).then((result) => {
if (result.success) {
return result;
}
promise.reject(new Error("error updating timestamp of topic"));
});
return promise;
}
static bulkOperation(topics, operation, options, tracked) {
const data = {
topic_ids: topics.mapBy("id"),
operation,
tracked,
};
if (options) {
if (options.select) {
data.silent = true;
}
}
return ajax("/topics/bulk", {
type: "PUT",
data,
});
}
static bulkOperationByFilter(filter, operation, options, tracked) {
const data = { filter, operation, tracked };
if (options) {
if (options.categoryId) {
data.category_id = options.categoryId;
}
if (options.includeSubcategories) {
data.include_subcategories = true;
}
if (options.tagName) {
data.tag_name = options.tagName;
}
if (options.private_message_inbox) {
data.private_message_inbox = options.private_message_inbox;
if (options.group_name) {
data.group_name = options.group_name;
}
}
}
return ajax("/topics/bulk", {
type: "PUT",
data,
});
}
static resetNew(category, include_subcategories, opts = {}) {
let { tracked, tag, topicIds } = {
tracked: false,
tag: null,
topicIds: null,
...opts,
};
const data = { tracked };
if (category) {
data.category_id = category.id;
data.include_subcategories = include_subcategories;
}
if (tag) {
data.tag_id = tag.id;
}
if (topicIds) {
data.topic_ids = topicIds;
}
if (opts.dismissPosts) {
data.dismiss_posts = opts.dismissPosts;
}
if (opts.dismissTopics) {
data.dismiss_topics = opts.dismissTopics;
}
if (opts.untrack) {
data.untrack = opts.untrack;
}
return ajax("/topics/reset-new", { type: "PUT", data });
}
static pmResetNew(opts = {}) {
const data = {};
if (opts.topicIds) {
data.topic_ids = opts.topicIds;
}
if (opts.inbox) {
data.inbox = opts.inbox;
if (opts.groupName) {
data.group_name = opts.groupName;
}
}
return ajax("/topics/pm-reset-new", { type: "PUT", data });
}
static idForSlug(slug) {
return ajax(`/t/id_for/${slug}`);
}
static setSlowMode(topicId, seconds, enabledUntil) {
const data = { seconds };
data.enabled_until = enabledUntil;
return ajax(`/t/${topicId}/slow_mode`, { type: "PUT", data });
}
static async applyTransformations(topics) {
await applyModelTransformations("topic", topics);
}
message = null;
errorLoading = false;
@alias("lastPoster.user") lastPosterUser;
@alias("lastPoster.primary_group") lastPosterGroup;
@alias("details.allowed_groups") allowedGroups;
@notEmpty("deleted_at") deleted;
@fmt("url", "%@/print") printUrl;
@equal("archetype", "private_message") isPrivateMessage;
@equal("archetype", "banner") isBanner;
@alias("bookmarks.length") bookmarkCount;
@and("pinned", "category.isUncategorizedCategory") isPinnedUncategorized;
@notEmpty("excerpt") hasExcerpt;
@propertyEqual("last_read_post_number", "highest_post_number") readLastPost;
@and("pinned", "readLastPost") canClearPin;
@or("details.can_edit", "details.can_edit_tags") canEditTags;
@discourseComputed("last_read_post_number", "highest_post_number")
visited(lastReadPostNumber, highestPostNumber) {
// >= to handle case where there are deleted posts at the end of the topic
return lastReadPostNumber >= highestPostNumber;
},
}
@discourseComputed("posters.firstObject")
creator(poster) {
return poster && poster.user;
},
}
@discourseComputed("posters.[]")
lastPoster(posters) {
@ -66,11 +323,7 @@ const Topic = RestModel.extend({
const latest = posters.filter((p) => p.extras?.includes("latest"))[0];
return latest || posters.firstObject;
}
},
lastPosterUser: alias("lastPoster.user"),
lastPosterGroup: alias("lastPoster.primary_group"),
allowedGroups: alias("details.allowed_groups"),
}
@discourseComputed("posters.[]", "participants.[]", "allowed_user_count")
featuredUsers(posters, participants, allowedUserCount) {
@ -109,12 +362,12 @@ const Topic = RestModel.extend({
}
return users;
},
}
@discourseComputed("fancy_title")
fancyTitle(title) {
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
},
}
// returns createdAt if there's no bumped date
@discourseComputed("bumped_at", "createdAt")
@ -124,7 +377,7 @@ const Topic = RestModel.extend({
} else {
return createdAt;
}
},
}
@discourseComputed("bumpedAt", "createdAt")
bumpedAtTitle(bumpedAt, createdAt) {
@ -139,12 +392,12 @@ const Topic = RestModel.extend({
})}\n${I18n.t("topic.bumped_at", { date: longDate(bumpedAt) })}`
: I18n.t("topic.created_at", { date: longDate(createdAt) });
}
},
}
@discourseComputed("created_at")
createdAt(created_at) {
return new Date(created_at);
},
}
@discourseComputed
postStream() {
@ -152,7 +405,7 @@ const Topic = RestModel.extend({
id: this.id,
topic: this,
});
},
}
@discourseComputed("tags")
visibleListTags(tags) {
@ -170,66 +423,62 @@ const Topic = RestModel.extend({
});
return newTags;
},
}
@discourseComputed("related_messages")
relatedMessages(relatedMessages) {
if (relatedMessages) {
return relatedMessages.map((st) => this.store.createRecord("topic", st));
}
},
}
@discourseComputed("suggested_topics")
suggestedTopics(suggestedTopics) {
if (suggestedTopics) {
return suggestedTopics.map((st) => this.store.createRecord("topic", st));
}
},
}
@discourseComputed("posts_count")
replyCount(postsCount) {
return postsCount - 1;
},
}
get details() {
return (this._details ??= this.store.createRecord("topicDetails", {
id: this.id,
topic: this,
}));
},
}
set details(value) {
this._details = value;
},
}
@discourseComputed("visible")
invisible(visible) {
return visible !== undefined ? !visible : undefined;
},
deleted: notEmpty("deleted_at"),
}
@discourseComputed("id")
searchContext(id) {
return { type: "topic", id };
},
}
@computed("category_id")
get category() {
return Category.findById(this.category_id);
},
}
set category(newCategory) {
this.set("category_id", newCategory?.id);
},
}
@discourseComputed("url")
shareUrl(url) {
const user = User.current();
return resolveShareUrl(url, user);
},
printUrl: fmt("url", "%@/print"),
}
@discourseComputed("id", "slug")
url(id, slug) {
@ -238,7 +487,7 @@ const Topic = RestModel.extend({
slug = "topic";
}
return `${getURL("/t/")}${slug}/${id}`;
},
}
// Helper to build a Url with a post number
urlForPostNumber(postNumber) {
@ -247,7 +496,7 @@ const Topic = RestModel.extend({
url += `/${postNumber}`;
}
return url;
},
}
@discourseComputed("unread_posts", "new_posts")
totalUnread(unreadPosts, newPosts) {
@ -255,7 +504,7 @@ const Topic = RestModel.extend({
id: "discourse.topic.totalUnread",
});
return unreadPosts || newPosts;
},
}
@discourseComputed("unread_posts", "new_posts")
displayNewPosts(unreadPosts, newPosts) {
@ -264,12 +513,12 @@ const Topic = RestModel.extend({
{ id: "discourse.topic.totalUnread" }
);
return unreadPosts || newPosts;
},
}
@discourseComputed("last_read_post_number", "url")
lastReadUrl(lastReadPostNumber) {
return this.urlForPostNumber(lastReadPostNumber);
},
}
@discourseComputed("last_read_post_number", "highest_post_number", "url")
lastUnreadUrl(lastReadPostNumber, highestPostNumber) {
@ -299,28 +548,28 @@ const Topic = RestModel.extend({
}
return this.urlForPostNumber(postNumber);
},
}
@discourseComputed("highest_post_number", "url")
lastPostUrl(highestPostNumber) {
return this.urlForPostNumber(highestPostNumber);
},
}
@discourseComputed("url")
firstPostUrl() {
return this.urlForPostNumber(1);
},
}
@discourseComputed("url")
summaryUrl() {
const summaryQueryString = this.has_summary ? "?filter=summary" : "";
return `${this.urlForPostNumber(1)}${summaryQueryString}`;
},
}
@discourseComputed("last_poster.username")
lastPosterUrl(username) {
return userPath(username);
},
}
@discourseComputed("views")
viewsHeat(v) {
@ -334,20 +583,17 @@ const Topic = RestModel.extend({
return "heatmap-low";
}
return null;
},
}
@discourseComputed("archetype")
archetypeObject(archetype) {
return Site.currentProp("archetypes").findBy("id", archetype);
},
isPrivateMessage: equal("archetype", "private_message"),
isBanner: equal("archetype", "banner"),
}
toggleStatus(property) {
this.toggleProperty(property);
return this.saveStatus(property, !!this.get(property));
},
}
saveStatus(property, value, until) {
if (property === "closed") {
@ -361,23 +607,23 @@ const Topic = RestModel.extend({
until,
},
});
},
}
makeBanner() {
return ajax(`/t/${this.id}/make-banner`, { type: "PUT" }).then(() =>
this.set("archetype", "banner")
);
},
}
removeBanner() {
return ajax(`/t/${this.id}/remove-banner`, {
type: "PUT",
}).then(() => this.set("archetype", "regular"));
},
}
afterPostBookmarked(post) {
post.set("bookmarked", true);
},
}
firstPost() {
const postStream = this.postStream;
@ -393,7 +639,7 @@ const Topic = RestModel.extend({
} else {
return this.postStream.loadPostByPostNumber(1);
}
},
}
postById(id) {
const loaded = this.postStream.findLoadedPost(id);
@ -402,13 +648,11 @@ const Topic = RestModel.extend({
}
return this.postStream.loadPost(id);
},
}
deleteBookmarks() {
return ajax(`/t/${this.id}/remove_bookmarks`, { type: "PUT" });
},
bookmarkCount: alias("bookmarks.length"),
}
removeBookmark(id) {
if (!this.bookmarks) {
@ -430,7 +674,7 @@ const Topic = RestModel.extend({
);
this.set("bookmarked", this.bookmarks.length);
this.incrementProperty("bookmarksWereChanged");
},
}
clearBookmarks() {
this.toggleProperty("bookmarked");
@ -447,28 +691,28 @@ const Topic = RestModel.extend({
this.set("bookmarks", []);
return postIds;
},
}
createGroupInvite(group) {
return ajax(`/t/${this.id}/invite-group`, {
type: "POST",
data: { group },
});
},
}
createInvite(user, group_ids, custom_message) {
return ajax(`/t/${this.id}/invite`, {
type: "POST",
data: { user, group_ids, custom_message },
});
},
}
generateInviteLink(email, group_ids, topic_id) {
return ajax("/invites", {
type: "POST",
data: { email, skip_email: true, group_ids, topic_id },
});
},
}
// Delete this topic
destroy(deleted_by, opts = {}) {
@ -502,7 +746,7 @@ const Topic = RestModel.extend({
}
})
.catch(popupAjaxError);
},
}
// Recover this topic if deleted
recover() {
@ -516,7 +760,7 @@ const Topic = RestModel.extend({
data: { context: window.location.pathname },
type: "PUT",
});
},
}
// Update our attributes from a JSON result
updateFromJson(json) {
@ -543,15 +787,13 @@ const Topic = RestModel.extend({
}
return this;
},
}
reload() {
return ajax(`/t/${this.id}`, { type: "GET" }).then((topic_json) =>
this.updateFromJson(topic_json)
);
},
isPinnedUncategorized: and("pinned", "category.isUncategorizedCategory"),
}
clearPin() {
// Clear the pin optimistically from the object
@ -563,7 +805,7 @@ const Topic = RestModel.extend({
// On error, put the pin back
this.setProperties({ pinned: true, unpinned: false });
});
},
}
togglePinnedForUser() {
if (this.pinned) {
@ -571,7 +813,7 @@ const Topic = RestModel.extend({
} else {
this.rePin();
}
},
}
rePin() {
// Clear the pin optimistically from the object
@ -583,23 +825,17 @@ const Topic = RestModel.extend({
// On error, put the pin back
this.setProperties({ pinned: true, unpinned: false });
});
},
}
@discourseComputed("excerpt")
escapedExcerpt(excerpt) {
return emojiUnescape(excerpt);
},
hasExcerpt: notEmpty("excerpt"),
}
@discourseComputed("excerpt")
excerptTruncated(excerpt) {
return excerpt && excerpt.slice(-8) === "&hellip;";
},
readLastPost: propertyEqual("last_read_post_number", "highest_post_number"),
canClearPin: and("pinned", "readLastPost"),
canEditTags: or("details.can_edit", "details.can_edit_tags"),
}
archiveMessage() {
this.set("archiving", true);
@ -617,7 +853,7 @@ const Topic = RestModel.extend({
.finally(() => this.set("archiving", false));
return promise;
},
}
moveToInbox() {
this.set("archiving", true);
@ -633,7 +869,7 @@ const Topic = RestModel.extend({
.finally(() => this.set("archiving", false));
return promise;
},
}
publish() {
return ajax(`/t/${this.id}/publish`, {
@ -642,7 +878,7 @@ const Topic = RestModel.extend({
})
.then(() => this.set("destination_category_id", null))
.catch(popupAjaxError);
},
}
updateDestinationCategory(categoryId) {
this.set("destination_category_id", categoryId);
@ -650,7 +886,7 @@ const Topic = RestModel.extend({
type: "PUT",
data: { category_id: categoryId },
});
},
}
convertTopic(type, opts) {
let args = { type: "PUT" };
@ -658,13 +894,13 @@ const Topic = RestModel.extend({
args.data = { category_id: opts.categoryId };
}
return ajax(`/t/${this.id}/convert-topic/${type}`, args);
},
}
resetBumpDate() {
return ajax(`/t/${this.id}/reset-bump-date`, { type: "PUT" }).catch(
popupAjaxError
);
},
}
updateTags(tags) {
if (!tags || tags.length === 0) {
@ -675,254 +911,9 @@ const Topic = RestModel.extend({
type: "PUT",
data: { tags },
});
},
});
Topic.reopenClass({
NotificationLevel: {
WATCHING: 3,
TRACKING: 2,
REGULAR: 1,
MUTED: 0,
},
munge(json) {
// ensure we are not overriding category computed property
delete json.category;
json.bookmarks = json.bookmarks || [];
return json;
},
createActionSummary(result) {
if (result.actions_summary) {
const lookup = EmberObject.create();
result.actions_summary = result.actions_summary.map((a) => {
a.post = result;
a.actionType = Site.current().postActionTypeById(a.id);
const actionSummary = ActionSummary.create(a);
lookup.set(a.actionType.get("name_key"), actionSummary);
return actionSummary;
});
result.set("actionByName", lookup);
}
},
update(topic, props, opts = {}) {
// We support `category_id` and `categoryId` for compatibility
if (typeof props.categoryId !== "undefined") {
props.category_id = props.categoryId;
delete props.categoryId;
}
// Make sure we never change the category for private messages
if (topic.get("isPrivateMessage")) {
delete props.category_id;
}
const data = { ...props };
if (opts.fastEdit) {
data.keep_existing_draft = true;
}
return ajax(topic.get("url"), {
type: "PUT",
data: JSON.stringify(data),
contentType: "application/json",
}).then((result) => {
// The title can be cleaned up server side
props.title = result.basic_topic.title;
props.fancy_title = result.basic_topic.fancy_title;
if (topic.is_shared_draft) {
props.destination_category_id = props.category_id;
delete props.category_id;
}
topic.setProperties(props);
});
},
create() {
const result = this._super.apply(this, arguments);
this.createActionSummary(result);
return result;
},
// Load a topic, but accepts a set of filters
find(topicId, opts) {
let url = getURL("/t/") + topicId;
if (opts.nearPost) {
url += `/${opts.nearPost}`;
}
const data = {};
if (opts.postsAfter) {
data.posts_after = opts.postsAfter;
}
if (opts.postsBefore) {
data.posts_before = opts.postsBefore;
}
if (opts.trackVisit) {
data.track_visit = true;
}
// Add username filters if we have them
if (opts.userFilters && opts.userFilters.length > 0) {
data.username_filters = [];
opts.userFilters.forEach(function (username) {
data.username_filters.push(username);
});
}
// Add the summary of filter if we have it
if (opts.summary === true) {
data.summary = true;
}
// Check the preload store. If not, load it via JSON
return ajax(`${url}.json`, { data });
},
changeOwners(topicId, opts) {
const promise = ajax(`/t/${topicId}/change-owner`, {
type: "POST",
data: opts,
}).then((result) => {
if (result.success) {
return result;
}
promise.reject(new Error("error changing ownership of posts"));
});
return promise;
},
changeTimestamp(topicId, timestamp) {
const promise = ajax(`/t/${topicId}/change-timestamp`, {
type: "PUT",
data: { timestamp },
}).then((result) => {
if (result.success) {
return result;
}
promise.reject(new Error("error updating timestamp of topic"));
});
return promise;
},
bulkOperation(topics, operation, options, tracked) {
const data = {
topic_ids: topics.mapBy("id"),
operation,
tracked,
};
if (options) {
if (options.select) {
data.silent = true;
}
}
return ajax("/topics/bulk", {
type: "PUT",
data,
});
},
bulkOperationByFilter(filter, operation, options, tracked) {
const data = { filter, operation, tracked };
if (options) {
if (options.categoryId) {
data.category_id = options.categoryId;
}
if (options.includeSubcategories) {
data.include_subcategories = true;
}
if (options.tagName) {
data.tag_name = options.tagName;
}
if (options.private_message_inbox) {
data.private_message_inbox = options.private_message_inbox;
if (options.group_name) {
data.group_name = options.group_name;
}
}
}
return ajax("/topics/bulk", {
type: "PUT",
data,
});
},
resetNew(category, include_subcategories, opts = {}) {
let { tracked, tag, topicIds } = {
tracked: false,
tag: null,
topicIds: null,
...opts,
};
const data = { tracked };
if (category) {
data.category_id = category.id;
data.include_subcategories = include_subcategories;
}
if (tag) {
data.tag_id = tag.id;
}
if (topicIds) {
data.topic_ids = topicIds;
}
if (opts.dismissPosts) {
data.dismiss_posts = opts.dismissPosts;
}
if (opts.dismissTopics) {
data.dismiss_topics = opts.dismissTopics;
}
if (opts.untrack) {
data.untrack = opts.untrack;
}
return ajax("/topics/reset-new", { type: "PUT", data });
},
pmResetNew(opts = {}) {
const data = {};
if (opts.topicIds) {
data.topic_ids = opts.topicIds;
}
if (opts.inbox) {
data.inbox = opts.inbox;
if (opts.groupName) {
data.group_name = opts.groupName;
}
}
return ajax("/topics/pm-reset-new", { type: "PUT", data });
},
idForSlug(slug) {
return ajax(`/t/id_for/${slug}`);
},
setSlowMode(topicId, seconds, enabledUntil) {
const data = { seconds };
data.enabled_until = enabledUntil;
return ajax(`/t/${topicId}/slow_mode`, { type: "PUT", data });
},
async applyTransformations(topics) {
await applyModelTransformations("topic", topics);
},
});
function moveResult(result) {
if (result.success) {
// We should be hesitant to flush the map but moving ids is one rare case
@ -952,5 +943,3 @@ export function registerCustomLastUnreadUrlCallback(fn) {
export function clearCustomLastUnreadUrlCallbacks() {
_customLastUnreadUrlCallbacks.clear();
}
export default Topic;