mirror of
https://github.com/discourse/discourse.git
synced 2025-02-19 04:22:45 +08:00
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:
parent
6c597b648b
commit
a3ef9e92eb
|
@ -18,8 +18,99 @@ import User from "discourse/models/user";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import I18n from "discourse-i18n";
|
import I18n from "discourse-i18n";
|
||||||
|
|
||||||
const Post = RestModel.extend({
|
export default class Post extends RestModel {
|
||||||
customShare: null,
|
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")
|
@discourseComputed("url", "customShare")
|
||||||
shareUrl(url) {
|
shareUrl(url) {
|
||||||
|
@ -29,30 +120,22 @@ const Post = RestModel.extend({
|
||||||
|
|
||||||
const user = User.current();
|
const user = User.current();
|
||||||
return resolveShareUrl(url, user);
|
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")
|
@discourseComputed("name", "username")
|
||||||
showName(name, username) {
|
showName(name, username) {
|
||||||
return name && name !== username && this.siteSettings.display_name_on_posts;
|
return name && name !== username && this.siteSettings.display_name_on_posts;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("firstPost", "deleted_by", "topic.deleted_by")
|
@discourseComputed("firstPost", "deleted_by", "topic.deleted_by")
|
||||||
postDeletedBy(firstPost, deletedBy, topicDeletedBy) {
|
postDeletedBy(firstPost, deletedBy, topicDeletedBy) {
|
||||||
return firstPost ? topicDeletedBy : deletedBy;
|
return firstPost ? topicDeletedBy : deletedBy;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("firstPost", "deleted_at", "topic.deleted_at")
|
@discourseComputed("firstPost", "deleted_at", "topic.deleted_at")
|
||||||
postDeletedAt(firstPost, deletedAt, topicDeletedAt) {
|
postDeletedAt(firstPost, deletedAt, topicDeletedAt) {
|
||||||
return firstPost ? topicDeletedAt : deletedAt;
|
return firstPost ? topicDeletedAt : deletedAt;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("post_number", "topic_id", "topic.slug")
|
@discourseComputed("post_number", "topic_id", "topic.slug")
|
||||||
url(post_number, topic_id, topicSlug) {
|
url(post_number, topic_id, topicSlug) {
|
||||||
|
@ -61,18 +144,18 @@ const Post = RestModel.extend({
|
||||||
topic_id || this.get("topic.id"),
|
topic_id || this.get("topic.id"),
|
||||||
post_number
|
post_number
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Don't drop the /1
|
// Don't drop the /1
|
||||||
@discourseComputed("post_number", "url")
|
@discourseComputed("post_number", "url")
|
||||||
urlWithNumber(postNumber, baseUrl) {
|
urlWithNumber(postNumber, baseUrl) {
|
||||||
return postNumber === 1 ? `${baseUrl}/1` : baseUrl;
|
return postNumber === 1 ? `${baseUrl}/1` : baseUrl;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("username")
|
@discourseComputed("username")
|
||||||
usernameUrl: userPath,
|
usernameUrl(username) {
|
||||||
|
return userPath(username);
|
||||||
topicOwner: propertyEqual("topic.details.created_by.id", "user_id"),
|
}
|
||||||
|
|
||||||
updatePostField(field, value) {
|
updatePostField(field, value) {
|
||||||
const data = {};
|
const data = {};
|
||||||
|
@ -81,7 +164,7 @@ const Post = RestModel.extend({
|
||||||
return ajax(`/posts/${this.id}/${field}`, { type: "PUT", data })
|
return ajax(`/posts/${this.id}/${field}`, { type: "PUT", data })
|
||||||
.then(() => this.set(field, value))
|
.then(() => this.set(field, value))
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("link_counts.@each.internal")
|
@discourseComputed("link_counts.@each.internal")
|
||||||
internalLinks() {
|
internalLinks() {
|
||||||
|
@ -90,7 +173,7 @@ const Post = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.link_counts.filterBy("internal").filterBy("title");
|
return this.link_counts.filterBy("internal").filterBy("title");
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("actions_summary.@each.can_act")
|
@discourseComputed("actions_summary.@each.can_act")
|
||||||
flagsAvailable() {
|
flagsAvailable() {
|
||||||
|
@ -103,7 +186,7 @@ const Post = RestModel.extend({
|
||||||
return this.site.flagTypes.filter((item) =>
|
return this.site.flagTypes.filter((item) =>
|
||||||
this.get(`actionByName.${item.name_key}.can_act`)
|
this.get(`actionByName.${item.name_key}.can_act`)
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"siteSettings.use_pg_headlines_for_excerpt",
|
"siteSettings.use_pg_headlines_for_excerpt",
|
||||||
|
@ -111,25 +194,25 @@ const Post = RestModel.extend({
|
||||||
)
|
)
|
||||||
useTopicTitleHeadline(enabled, title) {
|
useTopicTitleHeadline(enabled, title) {
|
||||||
return enabled && title;
|
return enabled && title;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("topic_title_headline")
|
@discourseComputed("topic_title_headline")
|
||||||
topicTitleHeadline(title) {
|
topicTitleHeadline(title) {
|
||||||
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
||||||
},
|
}
|
||||||
|
|
||||||
afterUpdate(res) {
|
afterUpdate(res) {
|
||||||
if (res.category) {
|
if (res.category) {
|
||||||
this.site.updateCategory(res.category);
|
this.site.updateCategory(res.category);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
updateProperties() {
|
updateProperties() {
|
||||||
return {
|
return {
|
||||||
post: { raw: this.raw, edit_reason: this.editReason },
|
post: { raw: this.raw, edit_reason: this.editReason },
|
||||||
image_sizes: this.imageSizes,
|
image_sizes: this.imageSizes,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
createProperties() {
|
createProperties() {
|
||||||
// composer only used once, defer the dependency
|
// composer only used once, defer the dependency
|
||||||
|
@ -148,7 +231,7 @@ const Post = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Expands the first post's content, if embedded and shortened.
|
// Expands the first post's content, if embedded and shortened.
|
||||||
expand() {
|
expand() {
|
||||||
|
@ -158,7 +241,7 @@ const Post = RestModel.extend({
|
||||||
`<section class="expanded-embed">${post.cooked}</section>`
|
`<section class="expanded-embed">${post.cooked}</section>`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// Recover a deleted post
|
// Recover a deleted post
|
||||||
recover() {
|
recover() {
|
||||||
|
@ -192,7 +275,7 @@ const Post = RestModel.extend({
|
||||||
popupAjaxError(error);
|
popupAjaxError(error);
|
||||||
this.setProperties(initProperties);
|
this.setProperties(initProperties);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Changes the state of the post to be deleted. Does not call the server, that should be
|
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();
|
return promise || Promise.resolve();
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Changes the state of the post to NOT be deleted. Does not call the server.
|
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,
|
user_deleted: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
destroy(deletedBy, opts) {
|
destroy(deletedBy, opts) {
|
||||||
return this.setDeletedState(deletedBy).then(() => {
|
return this.setDeletedState(deletedBy).then(() => {
|
||||||
|
@ -259,7 +342,7 @@ const Post = RestModel.extend({
|
||||||
type: "DELETE",
|
type: "DELETE",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Updates a post from another's attributes. This will normally happen when a post is loading but
|
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() {
|
expandHidden() {
|
||||||
return ajax(`/posts/${this.id}/cooked.json`).then((result) => {
|
return ajax(`/posts/${this.id}/cooked.json`).then((result) => {
|
||||||
this.setProperties({ cooked: result.cooked, cooked_hidden: false });
|
this.setProperties({ cooked: result.cooked, cooked_hidden: false });
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
rebake() {
|
rebake() {
|
||||||
return ajax(`/posts/${this.id}/rebake`, { type: "PUT" }).catch(
|
return ajax(`/posts/${this.id}/rebake`, { type: "PUT" }).catch(
|
||||||
popupAjaxError
|
popupAjaxError
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
unhide() {
|
unhide() {
|
||||||
return ajax(`/posts/${this.id}/unhide`, { type: "PUT" });
|
return ajax(`/posts/${this.id}/unhide`, { type: "PUT" });
|
||||||
},
|
}
|
||||||
|
|
||||||
createBookmark(data) {
|
createBookmark(data) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -324,12 +407,12 @@ const Post = RestModel.extend({
|
||||||
targetId: this.id,
|
targetId: this.id,
|
||||||
});
|
});
|
||||||
this.appEvents.trigger("post-stream:refresh", { id: this.id });
|
this.appEvents.trigger("post-stream:refresh", { id: this.id });
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteBookmark(bookmarked) {
|
deleteBookmark(bookmarked) {
|
||||||
this.set("topic.bookmarked", bookmarked);
|
this.set("topic.bookmarked", bookmarked);
|
||||||
this.clearBookmark();
|
this.clearBookmark();
|
||||||
},
|
}
|
||||||
|
|
||||||
clearBookmark() {
|
clearBookmark() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -344,14 +427,14 @@ const Post = RestModel.extend({
|
||||||
target: "post",
|
target: "post",
|
||||||
targetId: this.id,
|
targetId: this.id,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
updateActionsSummary(json) {
|
updateActionsSummary(json) {
|
||||||
if (json && json.id === this.id) {
|
if (json && json.id === this.id) {
|
||||||
json = Post.munge(json);
|
json = Post.munge(json);
|
||||||
this.set("actions_summary", json.actions_summary);
|
this.set("actions_summary", json.actions_summary);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
updateLikeCount(count, userId, eventType) {
|
updateLikeCount(count, userId, eventType) {
|
||||||
let ownAction = User.current()?.id === userId;
|
let ownAction = User.current()?.id === userId;
|
||||||
|
@ -385,101 +468,15 @@ const Post = RestModel.extend({
|
||||||
Object.assign(this.actionByName["like"], newActionObject);
|
Object.assign(this.actionByName["like"], newActionObject);
|
||||||
Object.assign(this.likeAction, newActionObject);
|
Object.assign(this.likeAction, newActionObject);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
revertToRevision(version) {
|
revertToRevision(version) {
|
||||||
return ajax(`/posts/${this.id}/revisions/${version}/revert`, {
|
return ajax(`/posts/${this.id}/revisions/${version}/revert`, {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
get topicNotificationLevel() {
|
get topicNotificationLevel() {
|
||||||
return this.topic.details.notification_level;
|
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;
|
|
||||||
|
|
|
@ -45,20 +45,277 @@ export function loadTopicView(topic, args) {
|
||||||
export const ID_CONSTRAINT = /^\d+$/;
|
export const ID_CONSTRAINT = /^\d+$/;
|
||||||
let _customLastUnreadUrlCallbacks = [];
|
let _customLastUnreadUrlCallbacks = [];
|
||||||
|
|
||||||
const Topic = RestModel.extend({
|
export default class Topic extends RestModel {
|
||||||
message: null,
|
static NotificationLevel = {
|
||||||
errorLoading: false,
|
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")
|
@discourseComputed("last_read_post_number", "highest_post_number")
|
||||||
visited(lastReadPostNumber, highestPostNumber) {
|
visited(lastReadPostNumber, highestPostNumber) {
|
||||||
// >= to handle case where there are deleted posts at the end of the topic
|
// >= to handle case where there are deleted posts at the end of the topic
|
||||||
return lastReadPostNumber >= highestPostNumber;
|
return lastReadPostNumber >= highestPostNumber;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("posters.firstObject")
|
@discourseComputed("posters.firstObject")
|
||||||
creator(poster) {
|
creator(poster) {
|
||||||
return poster && poster.user;
|
return poster && poster.user;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("posters.[]")
|
@discourseComputed("posters.[]")
|
||||||
lastPoster(posters) {
|
lastPoster(posters) {
|
||||||
|
@ -66,11 +323,7 @@ const Topic = RestModel.extend({
|
||||||
const latest = posters.filter((p) => p.extras?.includes("latest"))[0];
|
const latest = posters.filter((p) => p.extras?.includes("latest"))[0];
|
||||||
return latest || posters.firstObject;
|
return latest || posters.firstObject;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
lastPosterUser: alias("lastPoster.user"),
|
|
||||||
lastPosterGroup: alias("lastPoster.primary_group"),
|
|
||||||
allowedGroups: alias("details.allowed_groups"),
|
|
||||||
|
|
||||||
@discourseComputed("posters.[]", "participants.[]", "allowed_user_count")
|
@discourseComputed("posters.[]", "participants.[]", "allowed_user_count")
|
||||||
featuredUsers(posters, participants, allowedUserCount) {
|
featuredUsers(posters, participants, allowedUserCount) {
|
||||||
|
@ -109,12 +362,12 @@ const Topic = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return users;
|
return users;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("fancy_title")
|
@discourseComputed("fancy_title")
|
||||||
fancyTitle(title) {
|
fancyTitle(title) {
|
||||||
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
return fancyTitle(title, this.siteSettings.support_mixed_text_direction);
|
||||||
},
|
}
|
||||||
|
|
||||||
// returns createdAt if there's no bumped date
|
// returns createdAt if there's no bumped date
|
||||||
@discourseComputed("bumped_at", "createdAt")
|
@discourseComputed("bumped_at", "createdAt")
|
||||||
|
@ -124,7 +377,7 @@ const Topic = RestModel.extend({
|
||||||
} else {
|
} else {
|
||||||
return createdAt;
|
return createdAt;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("bumpedAt", "createdAt")
|
@discourseComputed("bumpedAt", "createdAt")
|
||||||
bumpedAtTitle(bumpedAt, createdAt) {
|
bumpedAtTitle(bumpedAt, createdAt) {
|
||||||
|
@ -139,12 +392,12 @@ const Topic = RestModel.extend({
|
||||||
})}\n${I18n.t("topic.bumped_at", { date: longDate(bumpedAt) })}`
|
})}\n${I18n.t("topic.bumped_at", { date: longDate(bumpedAt) })}`
|
||||||
: I18n.t("topic.created_at", { date: longDate(createdAt) });
|
: I18n.t("topic.created_at", { date: longDate(createdAt) });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("created_at")
|
@discourseComputed("created_at")
|
||||||
createdAt(created_at) {
|
createdAt(created_at) {
|
||||||
return new Date(created_at);
|
return new Date(created_at);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
postStream() {
|
postStream() {
|
||||||
|
@ -152,7 +405,7 @@ const Topic = RestModel.extend({
|
||||||
id: this.id,
|
id: this.id,
|
||||||
topic: this,
|
topic: this,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("tags")
|
@discourseComputed("tags")
|
||||||
visibleListTags(tags) {
|
visibleListTags(tags) {
|
||||||
|
@ -170,66 +423,62 @@ const Topic = RestModel.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
return newTags;
|
return newTags;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("related_messages")
|
@discourseComputed("related_messages")
|
||||||
relatedMessages(relatedMessages) {
|
relatedMessages(relatedMessages) {
|
||||||
if (relatedMessages) {
|
if (relatedMessages) {
|
||||||
return relatedMessages.map((st) => this.store.createRecord("topic", st));
|
return relatedMessages.map((st) => this.store.createRecord("topic", st));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("suggested_topics")
|
@discourseComputed("suggested_topics")
|
||||||
suggestedTopics(suggestedTopics) {
|
suggestedTopics(suggestedTopics) {
|
||||||
if (suggestedTopics) {
|
if (suggestedTopics) {
|
||||||
return suggestedTopics.map((st) => this.store.createRecord("topic", st));
|
return suggestedTopics.map((st) => this.store.createRecord("topic", st));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("posts_count")
|
@discourseComputed("posts_count")
|
||||||
replyCount(postsCount) {
|
replyCount(postsCount) {
|
||||||
return postsCount - 1;
|
return postsCount - 1;
|
||||||
},
|
}
|
||||||
|
|
||||||
get details() {
|
get details() {
|
||||||
return (this._details ??= this.store.createRecord("topicDetails", {
|
return (this._details ??= this.store.createRecord("topicDetails", {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
topic: this,
|
topic: this,
|
||||||
}));
|
}));
|
||||||
},
|
}
|
||||||
|
|
||||||
set details(value) {
|
set details(value) {
|
||||||
this._details = value;
|
this._details = value;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("visible")
|
@discourseComputed("visible")
|
||||||
invisible(visible) {
|
invisible(visible) {
|
||||||
return visible !== undefined ? !visible : undefined;
|
return visible !== undefined ? !visible : undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
deleted: notEmpty("deleted_at"),
|
|
||||||
|
|
||||||
@discourseComputed("id")
|
@discourseComputed("id")
|
||||||
searchContext(id) {
|
searchContext(id) {
|
||||||
return { type: "topic", id };
|
return { type: "topic", id };
|
||||||
},
|
}
|
||||||
|
|
||||||
@computed("category_id")
|
@computed("category_id")
|
||||||
get category() {
|
get category() {
|
||||||
return Category.findById(this.category_id);
|
return Category.findById(this.category_id);
|
||||||
},
|
}
|
||||||
|
|
||||||
set category(newCategory) {
|
set category(newCategory) {
|
||||||
this.set("category_id", newCategory?.id);
|
this.set("category_id", newCategory?.id);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("url")
|
@discourseComputed("url")
|
||||||
shareUrl(url) {
|
shareUrl(url) {
|
||||||
const user = User.current();
|
const user = User.current();
|
||||||
return resolveShareUrl(url, user);
|
return resolveShareUrl(url, user);
|
||||||
},
|
}
|
||||||
|
|
||||||
printUrl: fmt("url", "%@/print"),
|
|
||||||
|
|
||||||
@discourseComputed("id", "slug")
|
@discourseComputed("id", "slug")
|
||||||
url(id, slug) {
|
url(id, slug) {
|
||||||
|
@ -238,7 +487,7 @@ const Topic = RestModel.extend({
|
||||||
slug = "topic";
|
slug = "topic";
|
||||||
}
|
}
|
||||||
return `${getURL("/t/")}${slug}/${id}`;
|
return `${getURL("/t/")}${slug}/${id}`;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Helper to build a Url with a post number
|
// Helper to build a Url with a post number
|
||||||
urlForPostNumber(postNumber) {
|
urlForPostNumber(postNumber) {
|
||||||
|
@ -247,7 +496,7 @@ const Topic = RestModel.extend({
|
||||||
url += `/${postNumber}`;
|
url += `/${postNumber}`;
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("unread_posts", "new_posts")
|
@discourseComputed("unread_posts", "new_posts")
|
||||||
totalUnread(unreadPosts, newPosts) {
|
totalUnread(unreadPosts, newPosts) {
|
||||||
|
@ -255,7 +504,7 @@ const Topic = RestModel.extend({
|
||||||
id: "discourse.topic.totalUnread",
|
id: "discourse.topic.totalUnread",
|
||||||
});
|
});
|
||||||
return unreadPosts || newPosts;
|
return unreadPosts || newPosts;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("unread_posts", "new_posts")
|
@discourseComputed("unread_posts", "new_posts")
|
||||||
displayNewPosts(unreadPosts, newPosts) {
|
displayNewPosts(unreadPosts, newPosts) {
|
||||||
|
@ -264,12 +513,12 @@ const Topic = RestModel.extend({
|
||||||
{ id: "discourse.topic.totalUnread" }
|
{ id: "discourse.topic.totalUnread" }
|
||||||
);
|
);
|
||||||
return unreadPosts || newPosts;
|
return unreadPosts || newPosts;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("last_read_post_number", "url")
|
@discourseComputed("last_read_post_number", "url")
|
||||||
lastReadUrl(lastReadPostNumber) {
|
lastReadUrl(lastReadPostNumber) {
|
||||||
return this.urlForPostNumber(lastReadPostNumber);
|
return this.urlForPostNumber(lastReadPostNumber);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("last_read_post_number", "highest_post_number", "url")
|
@discourseComputed("last_read_post_number", "highest_post_number", "url")
|
||||||
lastUnreadUrl(lastReadPostNumber, highestPostNumber) {
|
lastUnreadUrl(lastReadPostNumber, highestPostNumber) {
|
||||||
|
@ -299,28 +548,28 @@ const Topic = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.urlForPostNumber(postNumber);
|
return this.urlForPostNumber(postNumber);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("highest_post_number", "url")
|
@discourseComputed("highest_post_number", "url")
|
||||||
lastPostUrl(highestPostNumber) {
|
lastPostUrl(highestPostNumber) {
|
||||||
return this.urlForPostNumber(highestPostNumber);
|
return this.urlForPostNumber(highestPostNumber);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("url")
|
@discourseComputed("url")
|
||||||
firstPostUrl() {
|
firstPostUrl() {
|
||||||
return this.urlForPostNumber(1);
|
return this.urlForPostNumber(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("url")
|
@discourseComputed("url")
|
||||||
summaryUrl() {
|
summaryUrl() {
|
||||||
const summaryQueryString = this.has_summary ? "?filter=summary" : "";
|
const summaryQueryString = this.has_summary ? "?filter=summary" : "";
|
||||||
return `${this.urlForPostNumber(1)}${summaryQueryString}`;
|
return `${this.urlForPostNumber(1)}${summaryQueryString}`;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("last_poster.username")
|
@discourseComputed("last_poster.username")
|
||||||
lastPosterUrl(username) {
|
lastPosterUrl(username) {
|
||||||
return userPath(username);
|
return userPath(username);
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("views")
|
@discourseComputed("views")
|
||||||
viewsHeat(v) {
|
viewsHeat(v) {
|
||||||
|
@ -334,20 +583,17 @@ const Topic = RestModel.extend({
|
||||||
return "heatmap-low";
|
return "heatmap-low";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("archetype")
|
@discourseComputed("archetype")
|
||||||
archetypeObject(archetype) {
|
archetypeObject(archetype) {
|
||||||
return Site.currentProp("archetypes").findBy("id", archetype);
|
return Site.currentProp("archetypes").findBy("id", archetype);
|
||||||
},
|
}
|
||||||
|
|
||||||
isPrivateMessage: equal("archetype", "private_message"),
|
|
||||||
isBanner: equal("archetype", "banner"),
|
|
||||||
|
|
||||||
toggleStatus(property) {
|
toggleStatus(property) {
|
||||||
this.toggleProperty(property);
|
this.toggleProperty(property);
|
||||||
return this.saveStatus(property, !!this.get(property));
|
return this.saveStatus(property, !!this.get(property));
|
||||||
},
|
}
|
||||||
|
|
||||||
saveStatus(property, value, until) {
|
saveStatus(property, value, until) {
|
||||||
if (property === "closed") {
|
if (property === "closed") {
|
||||||
|
@ -361,23 +607,23 @@ const Topic = RestModel.extend({
|
||||||
until,
|
until,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
makeBanner() {
|
makeBanner() {
|
||||||
return ajax(`/t/${this.id}/make-banner`, { type: "PUT" }).then(() =>
|
return ajax(`/t/${this.id}/make-banner`, { type: "PUT" }).then(() =>
|
||||||
this.set("archetype", "banner")
|
this.set("archetype", "banner")
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
removeBanner() {
|
removeBanner() {
|
||||||
return ajax(`/t/${this.id}/remove-banner`, {
|
return ajax(`/t/${this.id}/remove-banner`, {
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
}).then(() => this.set("archetype", "regular"));
|
}).then(() => this.set("archetype", "regular"));
|
||||||
},
|
}
|
||||||
|
|
||||||
afterPostBookmarked(post) {
|
afterPostBookmarked(post) {
|
||||||
post.set("bookmarked", true);
|
post.set("bookmarked", true);
|
||||||
},
|
}
|
||||||
|
|
||||||
firstPost() {
|
firstPost() {
|
||||||
const postStream = this.postStream;
|
const postStream = this.postStream;
|
||||||
|
@ -393,7 +639,7 @@ const Topic = RestModel.extend({
|
||||||
} else {
|
} else {
|
||||||
return this.postStream.loadPostByPostNumber(1);
|
return this.postStream.loadPostByPostNumber(1);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
postById(id) {
|
postById(id) {
|
||||||
const loaded = this.postStream.findLoadedPost(id);
|
const loaded = this.postStream.findLoadedPost(id);
|
||||||
|
@ -402,13 +648,11 @@ const Topic = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.postStream.loadPost(id);
|
return this.postStream.loadPost(id);
|
||||||
},
|
}
|
||||||
|
|
||||||
deleteBookmarks() {
|
deleteBookmarks() {
|
||||||
return ajax(`/t/${this.id}/remove_bookmarks`, { type: "PUT" });
|
return ajax(`/t/${this.id}/remove_bookmarks`, { type: "PUT" });
|
||||||
},
|
}
|
||||||
|
|
||||||
bookmarkCount: alias("bookmarks.length"),
|
|
||||||
|
|
||||||
removeBookmark(id) {
|
removeBookmark(id) {
|
||||||
if (!this.bookmarks) {
|
if (!this.bookmarks) {
|
||||||
|
@ -430,7 +674,7 @@ const Topic = RestModel.extend({
|
||||||
);
|
);
|
||||||
this.set("bookmarked", this.bookmarks.length);
|
this.set("bookmarked", this.bookmarks.length);
|
||||||
this.incrementProperty("bookmarksWereChanged");
|
this.incrementProperty("bookmarksWereChanged");
|
||||||
},
|
}
|
||||||
|
|
||||||
clearBookmarks() {
|
clearBookmarks() {
|
||||||
this.toggleProperty("bookmarked");
|
this.toggleProperty("bookmarked");
|
||||||
|
@ -447,28 +691,28 @@ const Topic = RestModel.extend({
|
||||||
this.set("bookmarks", []);
|
this.set("bookmarks", []);
|
||||||
|
|
||||||
return postIds;
|
return postIds;
|
||||||
},
|
}
|
||||||
|
|
||||||
createGroupInvite(group) {
|
createGroupInvite(group) {
|
||||||
return ajax(`/t/${this.id}/invite-group`, {
|
return ajax(`/t/${this.id}/invite-group`, {
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: { group },
|
data: { group },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
createInvite(user, group_ids, custom_message) {
|
createInvite(user, group_ids, custom_message) {
|
||||||
return ajax(`/t/${this.id}/invite`, {
|
return ajax(`/t/${this.id}/invite`, {
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: { user, group_ids, custom_message },
|
data: { user, group_ids, custom_message },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
generateInviteLink(email, group_ids, topic_id) {
|
generateInviteLink(email, group_ids, topic_id) {
|
||||||
return ajax("/invites", {
|
return ajax("/invites", {
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: { email, skip_email: true, group_ids, topic_id },
|
data: { email, skip_email: true, group_ids, topic_id },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// Delete this topic
|
// Delete this topic
|
||||||
destroy(deleted_by, opts = {}) {
|
destroy(deleted_by, opts = {}) {
|
||||||
|
@ -502,7 +746,7 @@ const Topic = RestModel.extend({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Recover this topic if deleted
|
// Recover this topic if deleted
|
||||||
recover() {
|
recover() {
|
||||||
|
@ -516,7 +760,7 @@ const Topic = RestModel.extend({
|
||||||
data: { context: window.location.pathname },
|
data: { context: window.location.pathname },
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// Update our attributes from a JSON result
|
// Update our attributes from a JSON result
|
||||||
updateFromJson(json) {
|
updateFromJson(json) {
|
||||||
|
@ -543,15 +787,13 @@ const Topic = RestModel.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
return ajax(`/t/${this.id}`, { type: "GET" }).then((topic_json) =>
|
return ajax(`/t/${this.id}`, { type: "GET" }).then((topic_json) =>
|
||||||
this.updateFromJson(topic_json)
|
this.updateFromJson(topic_json)
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
isPinnedUncategorized: and("pinned", "category.isUncategorizedCategory"),
|
|
||||||
|
|
||||||
clearPin() {
|
clearPin() {
|
||||||
// Clear the pin optimistically from the object
|
// Clear the pin optimistically from the object
|
||||||
|
@ -563,7 +805,7 @@ const Topic = RestModel.extend({
|
||||||
// On error, put the pin back
|
// On error, put the pin back
|
||||||
this.setProperties({ pinned: true, unpinned: false });
|
this.setProperties({ pinned: true, unpinned: false });
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
togglePinnedForUser() {
|
togglePinnedForUser() {
|
||||||
if (this.pinned) {
|
if (this.pinned) {
|
||||||
|
@ -571,7 +813,7 @@ const Topic = RestModel.extend({
|
||||||
} else {
|
} else {
|
||||||
this.rePin();
|
this.rePin();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
rePin() {
|
rePin() {
|
||||||
// Clear the pin optimistically from the object
|
// Clear the pin optimistically from the object
|
||||||
|
@ -583,23 +825,17 @@ const Topic = RestModel.extend({
|
||||||
// On error, put the pin back
|
// On error, put the pin back
|
||||||
this.setProperties({ pinned: true, unpinned: false });
|
this.setProperties({ pinned: true, unpinned: false });
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
@discourseComputed("excerpt")
|
@discourseComputed("excerpt")
|
||||||
escapedExcerpt(excerpt) {
|
escapedExcerpt(excerpt) {
|
||||||
return emojiUnescape(excerpt);
|
return emojiUnescape(excerpt);
|
||||||
},
|
}
|
||||||
|
|
||||||
hasExcerpt: notEmpty("excerpt"),
|
|
||||||
|
|
||||||
@discourseComputed("excerpt")
|
@discourseComputed("excerpt")
|
||||||
excerptTruncated(excerpt) {
|
excerptTruncated(excerpt) {
|
||||||
return excerpt && excerpt.slice(-8) === "…";
|
return excerpt && excerpt.slice(-8) === "…";
|
||||||
},
|
}
|
||||||
|
|
||||||
readLastPost: propertyEqual("last_read_post_number", "highest_post_number"),
|
|
||||||
canClearPin: and("pinned", "readLastPost"),
|
|
||||||
canEditTags: or("details.can_edit", "details.can_edit_tags"),
|
|
||||||
|
|
||||||
archiveMessage() {
|
archiveMessage() {
|
||||||
this.set("archiving", true);
|
this.set("archiving", true);
|
||||||
|
@ -617,7 +853,7 @@ const Topic = RestModel.extend({
|
||||||
.finally(() => this.set("archiving", false));
|
.finally(() => this.set("archiving", false));
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
},
|
}
|
||||||
|
|
||||||
moveToInbox() {
|
moveToInbox() {
|
||||||
this.set("archiving", true);
|
this.set("archiving", true);
|
||||||
|
@ -633,7 +869,7 @@ const Topic = RestModel.extend({
|
||||||
.finally(() => this.set("archiving", false));
|
.finally(() => this.set("archiving", false));
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
},
|
}
|
||||||
|
|
||||||
publish() {
|
publish() {
|
||||||
return ajax(`/t/${this.id}/publish`, {
|
return ajax(`/t/${this.id}/publish`, {
|
||||||
|
@ -642,7 +878,7 @@ const Topic = RestModel.extend({
|
||||||
})
|
})
|
||||||
.then(() => this.set("destination_category_id", null))
|
.then(() => this.set("destination_category_id", null))
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
}
|
||||||
|
|
||||||
updateDestinationCategory(categoryId) {
|
updateDestinationCategory(categoryId) {
|
||||||
this.set("destination_category_id", categoryId);
|
this.set("destination_category_id", categoryId);
|
||||||
|
@ -650,7 +886,7 @@ const Topic = RestModel.extend({
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data: { category_id: categoryId },
|
data: { category_id: categoryId },
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
convertTopic(type, opts) {
|
convertTopic(type, opts) {
|
||||||
let args = { type: "PUT" };
|
let args = { type: "PUT" };
|
||||||
|
@ -658,13 +894,13 @@ const Topic = RestModel.extend({
|
||||||
args.data = { category_id: opts.categoryId };
|
args.data = { category_id: opts.categoryId };
|
||||||
}
|
}
|
||||||
return ajax(`/t/${this.id}/convert-topic/${type}`, args);
|
return ajax(`/t/${this.id}/convert-topic/${type}`, args);
|
||||||
},
|
}
|
||||||
|
|
||||||
resetBumpDate() {
|
resetBumpDate() {
|
||||||
return ajax(`/t/${this.id}/reset-bump-date`, { type: "PUT" }).catch(
|
return ajax(`/t/${this.id}/reset-bump-date`, { type: "PUT" }).catch(
|
||||||
popupAjaxError
|
popupAjaxError
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
updateTags(tags) {
|
updateTags(tags) {
|
||||||
if (!tags || tags.length === 0) {
|
if (!tags || tags.length === 0) {
|
||||||
|
@ -675,254 +911,9 @@ const Topic = RestModel.extend({
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
data: { tags },
|
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) {
|
function moveResult(result) {
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// We should be hesitant to flush the map but moving ids is one rare case
|
// 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() {
|
export function clearCustomLastUnreadUrlCallbacks() {
|
||||||
_customLastUnreadUrlCallbacks.clear();
|
_customLastUnreadUrlCallbacks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Topic;
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user