DEV: Convert composer model to native class syntax (#25615)

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-09 10:43:55 +00:00 committed by GitHub
parent 3cc73cfd1e
commit d4b03d0ad2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 183 additions and 201 deletions

View File

@ -1,8 +1,11 @@
import { tracked } from "@glimmer/tracking";
import EmberObject, { set } from "@ember/object"; import EmberObject, { set } from "@ember/object";
import { dependentKeyCompat } from "@ember/object/compat";
import { and, equal, not, or, reads } from "@ember/object/computed"; import { and, equal, not, or, reads } from "@ember/object/computed";
import { next, throttle } from "@ember/runloop"; import { next, throttle } from "@ember/runloop";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { observes, on } from "@ember-decorators/object";
import { Promise } from "rsvp"; import { Promise } from "rsvp";
import { extractError, throwAjaxError } from "discourse/lib/ajax-error"; import { extractError, throwAjaxError } from "discourse/lib/ajax-error";
import { propertyNotEqual } from "discourse/lib/computed"; import { propertyNotEqual } from "discourse/lib/computed";
@ -16,10 +19,7 @@ import Topic from "discourse/models/topic";
import User from "discourse/models/user"; import User from "discourse/models/user";
import { tinyAvatar } from "discourse-common/lib/avatar-utils"; import { tinyAvatar } from "discourse-common/lib/avatar-utils";
import deprecated from "discourse-common/lib/deprecated"; import deprecated from "discourse-common/lib/deprecated";
import discourseComputed, { import discourseComputed from "discourse-common/utils/decorators";
observes,
on,
} from "discourse-common/utils/decorators";
import I18n from "discourse-i18n"; import I18n from "discourse-i18n";
let _customizations = []; let _customizations = [];
@ -115,28 +115,120 @@ export const SAVE_ICONS = {
[CREATE_SHARED_DRAFT]: "far-clipboard", [CREATE_SHARED_DRAFT]: "far-clipboard",
}; };
const Composer = RestModel.extend({ export default class Composer extends RestModel {
dialog: service(), // The status the compose view can have
_categoryId: null, static CLOSED = CLOSED;
unlistTopic: false, static SAVING = SAVING;
noBump: false, static OPEN = OPEN;
draftSaving: false, static DRAFT = DRAFT;
draftForceSave: false, static FULLSCREEN = FULLSCREEN;
showFullScreenExitPrompt: false,
archetypes: reads("site.archetypes"), // The actions the composer can take
static CREATE_TOPIC = CREATE_TOPIC;
static CREATE_SHARED_DRAFT = CREATE_SHARED_DRAFT;
static EDIT_SHARED_DRAFT = EDIT_SHARED_DRAFT;
static PRIVATE_MESSAGE = PRIVATE_MESSAGE;
static REPLY = REPLY;
static EDIT = EDIT;
sharedDraft: equal("action", CREATE_SHARED_DRAFT), // Draft key
static NEW_PRIVATE_MESSAGE_KEY = NEW_PRIVATE_MESSAGE_KEY;
static NEW_TOPIC_KEY = NEW_TOPIC_KEY;
@discourseComputed // TODO: Replace with injection
categoryId: { static create(args) {
get() { args = args || {};
args.user = args.user || User.current();
args.site = args.site || Site.current();
return super.create(args);
}
static serializeToTopic(fieldName, property) {
if (!property) {
property = fieldName;
}
_edit_topic_serializer[fieldName] = property;
}
static serializeOnCreate(fieldName, property) {
if (!property) {
property = fieldName;
}
_create_serializer[fieldName] = property;
}
static serializedFieldsForCreate() {
return Object.keys(_create_serializer);
}
static serializeOnUpdate(fieldName, property) {
if (!property) {
property = fieldName;
}
_update_serializer[fieldName] = property;
}
static serializedFieldsForUpdate() {
return Object.keys(_update_serializer);
}
static serializeToDraft(fieldName, property) {
if (!property) {
property = fieldName;
}
_draft_serializer[fieldName] = property;
_add_draft_fields[fieldName] = property;
}
static serializedFieldsForDraft() {
return Object.keys(_draft_serializer);
}
@service dialog;
unlistTopic = false;
noBump = false;
draftSaving = false;
draftForceSave = false;
showFullScreenExitPrompt = false;
@reads("site.archetypes") archetypes;
@equal("action", CREATE_SHARED_DRAFT) sharedDraft;
@equal("action", CREATE_TOPIC) creatingTopic;
@equal("action", CREATE_SHARED_DRAFT) creatingSharedDraft;
@equal("action", PRIVATE_MESSAGE) creatingPrivateMessage;
@not("creatingPrivateMessage") notCreatingPrivateMessage;
@not("privateMessage") notPrivateMessage;
@or("creatingTopic", "editingFirstPost") topicFirstPost;
@equal("action", REPLY) replyingToTopic;
@equal("composeState", OPEN) viewOpen;
@equal("composeState", DRAFT) viewDraft;
@equal("composeState", FULLSCREEN) viewFullscreen;
@or("viewOpen", "viewFullscreen") viewOpenOrFullscreen;
@and("editingPost", "post.firstPost") editingFirstPost;
@propertyNotEqual("reply", "originalText") replyDirty;
@propertyNotEqual("title", "originalTitle") titleDirty;
@or(
"creatingTopic",
"creatingPrivateMessage",
"editingFirstPost",
"creatingSharedDraft"
)
canEditTitle;
@and("canEditTitle", "notCreatingPrivateMessage", "notPrivateMessage")
canCategorize;
@tracked _categoryId = null;
@dependentKeyCompat
get categoryId() {
return this._categoryId; return this._categoryId;
}, }
// We wrap categoryId this way so we can fire `applyTopicTemplate` with // We wrap categoryId this way so we can fire `applyTopicTemplate` with
// the previous value as well as the new value // the previous value as well as the new value
set(categoryId) { set categoryId(categoryId) {
const oldCategoryId = this._categoryId; const oldCategoryId = this._categoryId;
if (this.privateMessage) { if (this.privateMessage) {
@ -157,56 +249,40 @@ const Composer = RestModel.extend({
if (oldCategoryId !== categoryId) { if (oldCategoryId !== categoryId) {
this.applyTopicTemplate(oldCategoryId, categoryId); this.applyTopicTemplate(oldCategoryId, categoryId);
} }
}
return categoryId;
},
},
@discourseComputed("categoryId") @discourseComputed("categoryId")
category(categoryId) { category(categoryId) {
return categoryId ? this.site.categories.findBy("id", categoryId) : null; return categoryId ? this.site.categories.findBy("id", categoryId) : null;
}, }
@discourseComputed("category.minimumRequiredTags") @discourseComputed("category.minimumRequiredTags")
minimumRequiredTags(minimumRequiredTags) { minimumRequiredTags(minimumRequiredTags) {
return minimumRequiredTags || 0; return minimumRequiredTags || 0;
}, }
creatingTopic: equal("action", CREATE_TOPIC),
creatingSharedDraft: equal("action", CREATE_SHARED_DRAFT),
creatingPrivateMessage: equal("action", PRIVATE_MESSAGE),
notCreatingPrivateMessage: not("creatingPrivateMessage"),
notPrivateMessage: not("privateMessage"),
@discourseComputed("editingPost", "topic.details.can_edit") @discourseComputed("editingPost", "topic.details.can_edit")
disableTitleInput(editingPost, canEditTopic) { disableTitleInput(editingPost, canEditTopic) {
return editingPost && !canEditTopic; return editingPost && !canEditTopic;
}, }
@discourseComputed("privateMessage", "archetype.hasOptions") @discourseComputed("privateMessage", "archetype.hasOptions")
showCategoryChooser(isPrivateMessage, hasOptions) { showCategoryChooser(isPrivateMessage, hasOptions) {
const manyCategories = this.site.categories.length > 1; const manyCategories = this.site.categories.length > 1;
return !isPrivateMessage && (hasOptions || manyCategories); return !isPrivateMessage && (hasOptions || manyCategories);
}, }
@discourseComputed("creatingPrivateMessage", "topic") @discourseComputed("creatingPrivateMessage", "topic")
privateMessage(creatingPrivateMessage, topic) { privateMessage(creatingPrivateMessage, topic) {
return ( return (
creatingPrivateMessage || (topic && topic.archetype === "private_message") creatingPrivateMessage || (topic && topic.archetype === "private_message")
); );
}, }
topicFirstPost: or("creatingTopic", "editingFirstPost"),
@discourseComputed("action") @discourseComputed("action")
editingPost: isEdit, editingPost(action) {
return isEdit(action);
replyingToTopic: equal("action", REPLY), }
viewOpen: equal("composeState", OPEN),
viewDraft: equal("composeState", DRAFT),
viewFullscreen: equal("composeState", FULLSCREEN),
viewOpenOrFullscreen: or("viewOpen", "viewFullscreen"),
@observes("composeState") @observes("composeState")
composeStateChanged() { composeStateChanged() {
@ -230,11 +306,9 @@ const Composer = RestModel.extend({
this.set("composerOpened", null); this.set("composerOpened", null);
elem.classList.remove("composer-open"); elem.classList.remove("composer-open");
} }
}, }
@discourseComputed get composerTime() {
composerTime: {
get() {
let total = this.composerTotalOpened || 0; let total = this.composerTotalOpened || 0;
const oldOpen = this.composerOpened; const oldOpen = this.composerOpened;
@ -243,18 +317,17 @@ const Composer = RestModel.extend({
} }
return total; return total;
}, }
},
@discourseComputed("archetypeId") @discourseComputed("archetypeId")
archetype(archetypeId) { archetype(archetypeId) {
return this.archetypes.findBy("id", archetypeId); return this.archetypes.findBy("id", archetypeId);
}, }
@observes("archetype") @observes("archetype")
archetypeChanged() { archetypeChanged() {
return this.set("metaData", EmberObject.create()); return this.set("metaData", EmberObject.create());
}, }
// called whenever the user types to update the typing time // called whenever the user types to update the typing time
typing() { typing() {
@ -267,22 +340,7 @@ const Composer = RestModel.extend({
100, 100,
false false
); );
}, }
editingFirstPost: and("editingPost", "post.firstPost"),
canEditTitle: or(
"creatingTopic",
"creatingPrivateMessage",
"editingFirstPost",
"creatingSharedDraft"
),
canCategorize: and(
"canEditTitle",
"notCreatingPrivateMessage",
"notPrivateMessage"
),
@discourseComputed( @discourseComputed(
"canEditTitle", "canEditTitle",
@ -322,14 +380,14 @@ const Composer = RestModel.extend({
!categoryIds.length || !categoryIds.length ||
categoryIds.includes(categoryId) categoryIds.includes(categoryId)
); );
}, }
@discourseComputed("canEditTopicFeaturedLink") @discourseComputed("canEditTopicFeaturedLink")
titlePlaceholder(canEditTopicFeaturedLink) { titlePlaceholder(canEditTopicFeaturedLink) {
return canEditTopicFeaturedLink return canEditTopicFeaturedLink
? "composer.title_or_link_placeholder" ? "composer.title_or_link_placeholder"
: "composer.title_placeholder"; : "composer.title_placeholder";
}, }
@discourseComputed("action", "post", "topic", "topic.title") @discourseComputed("action", "post", "topic", "topic.title")
replyOptions(action, post, topic, topicTitle) { replyOptions(action, post, topic, topicTitle) {
@ -381,7 +439,7 @@ const Composer = RestModel.extend({
} }
return options; return options;
}, }
@discourseComputed("targetRecipients") @discourseComputed("targetRecipients")
targetRecipientsArray(targetRecipients) { targetRecipientsArray(targetRecipients) {
@ -397,7 +455,7 @@ const Composer = RestModel.extend({
return { type: "user", name: item }; return { type: "user", name: item };
} }
}); });
}, }
@discourseComputed( @discourseComputed(
"loading", "loading",
@ -476,7 +534,7 @@ const Composer = RestModel.extend({
// has a category? (when needed) // has a category? (when needed)
return this.requiredCategoryMissing; return this.requiredCategoryMissing;
} }
}, }
@discourseComputed("canCategorize", "categoryId") @discourseComputed("canCategorize", "categoryId")
requiredCategoryMissing(canCategorize, categoryId) { requiredCategoryMissing(canCategorize, categoryId) {
@ -486,7 +544,7 @@ const Composer = RestModel.extend({
!this.siteSettings.allow_uncategorized_topics && !this.siteSettings.allow_uncategorized_topics &&
!!this._hasTopicTemplates !!this._hasTopicTemplates
); );
}, }
@discourseComputed("minimumTitleLength", "titleLength", "post.static_doc") @discourseComputed("minimumTitleLength", "titleLength", "post.static_doc")
titleLengthValid(minTitleLength, titleLength, staticDoc) { titleLengthValid(minTitleLength, titleLength, staticDoc) {
@ -497,21 +555,17 @@ const Composer = RestModel.extend({
return false; return false;
} }
return titleLength <= this.siteSettings.max_topic_title_length; return titleLength <= this.siteSettings.max_topic_title_length;
}, }
@discourseComputed("metaData") @discourseComputed("metaData")
hasMetaData(metaData) { hasMetaData(metaData) {
return metaData ? isEmpty(Object.keys(metaData)) : false; return metaData ? isEmpty(Object.keys(metaData)) : false;
}, }
replyDirty: propertyNotEqual("reply", "originalText"),
titleDirty: propertyNotEqual("title", "originalTitle"),
@discourseComputed("minimumTitleLength", "titleLength") @discourseComputed("minimumTitleLength", "titleLength")
missingTitleCharacters(minimumTitleLength, titleLength) { missingTitleCharacters(minimumTitleLength, titleLength) {
return minimumTitleLength - titleLength; return minimumTitleLength - titleLength;
}, }
@discourseComputed("privateMessage") @discourseComputed("privateMessage")
minimumTitleLength(privateMessage) { minimumTitleLength(privateMessage) {
@ -520,7 +574,7 @@ const Composer = RestModel.extend({
} else { } else {
return this.siteSettings.min_topic_title_length; return this.siteSettings.min_topic_title_length;
} }
}, }
@discourseComputed( @discourseComputed(
"minimumPostLength", "minimumPostLength",
@ -539,7 +593,7 @@ const Composer = RestModel.extend({
return 0; return 0;
} }
return minimumPostLength - replyLength; return minimumPostLength - replyLength;
}, }
@discourseComputed( @discourseComputed(
"privateMessage", "privateMessage",
@ -557,13 +611,13 @@ const Composer = RestModel.extend({
} else { } else {
return this.siteSettings.min_post_length; return this.siteSettings.min_post_length;
} }
}, }
@discourseComputed("title") @discourseComputed("title")
titleLength(title) { titleLength(title) {
title = title || ""; title = title || "";
return title.replace(/\s+/gim, " ").trim().length; return title.replace(/\s+/gim, " ").trim().length;
}, }
@discourseComputed("reply") @discourseComputed("reply")
replyLength(reply) { replyLength(reply) {
@ -630,12 +684,12 @@ const Composer = RestModel.extend({
} }
return len; return len;
}, }
@on("init") @on("init")
_setupComposer() { _setupComposer() {
this.set("archetypeId", this.site.default_archetype); this.set("archetypeId", this.site.default_archetype);
}, }
appendText(text, position, opts) { appendText(text, position, opts) {
const reply = this.reply || ""; const reply = this.reply || "";
@ -685,7 +739,7 @@ const Composer = RestModel.extend({
this.set("reply", before + text + after); this.set("reply", before + text + after);
return before.length + text.length; return before.length + text.length;
}, }
prependText(text, opts) { prependText(text, opts) {
const reply = this.reply || ""; const reply = this.reply || "";
@ -695,7 +749,7 @@ const Composer = RestModel.extend({
} }
this.set("reply", text + reply); this.set("reply", text + reply);
}, }
applyTopicTemplate(oldCategoryId, categoryId) { applyTopicTemplate(oldCategoryId, categoryId) {
if (this.action !== CREATE_TOPIC) { if (this.action !== CREATE_TOPIC) {
@ -720,7 +774,7 @@ const Composer = RestModel.extend({
if (category) { if (category) {
this.set("reply", category.topic_template || ""); this.set("reply", category.topic_template || "");
} }
}, }
/** /**
Open a composer Open a composer
@ -911,12 +965,12 @@ const Composer = RestModel.extend({
return promise.finally(() => { return promise.finally(() => {
this.set("loading", false); this.set("loading", false);
}); });
}, }
// Overwrite to implement custom logic // Overwrite to implement custom logic
beforeSave() { beforeSave() {
return Promise.resolve(); return Promise.resolve();
}, }
save(opts) { save(opts) {
return this.beforeSave().then(() => { return this.beforeSave().then(() => {
@ -928,7 +982,7 @@ const Composer = RestModel.extend({
return this.editingPost ? this.editPost(opts) : this.createPost(opts); return this.editingPost ? this.editPost(opts) : this.createPost(opts);
} }
}); });
}, }
clearState() { clearState() {
this.setProperties({ this.setProperties({
@ -946,12 +1000,12 @@ const Composer = RestModel.extend({
noBump: false, noBump: false,
editConflict: false, editConflict: false,
}); });
}, }
@discourseComputed("editConflict", "originalText") @discourseComputed("editConflict", "originalText")
rawOld(editConflict, originalText) { rawOld(editConflict, originalText) {
return editConflict ? null : originalText; return editConflict ? null : originalText;
}, }
editPost(opts) { editPost(opts) {
const post = this.post; const post = this.post;
@ -1018,7 +1072,7 @@ const Composer = RestModel.extend({
post.set("staged", false); post.set("staged", false);
this.appEvents.trigger("post-stream:refresh", { id: post.id }); this.appEvents.trigger("post-stream:refresh", { id: post.id });
}); });
}, }
serialize(serializer, dest) { serialize(serializer, dest) {
dest = dest || {}; dest = dest || {};
@ -1029,7 +1083,7 @@ const Composer = RestModel.extend({
} }
}); });
return dest; return dest;
}, }
async createPost(opts) { async createPost(opts) {
if (CREATE_TOPIC === this.action || PRIVATE_MESSAGE === this.action) { if (CREATE_TOPIC === this.action || PRIVATE_MESSAGE === this.action) {
@ -1164,7 +1218,7 @@ const Composer = RestModel.extend({
throw extractError(error); throw extractError(error);
} }
}, }
getCookedHtml() { getCookedHtml() {
const editorPreviewNode = document.querySelector( const editorPreviewNode = document.querySelector(
@ -1179,7 +1233,7 @@ const Composer = RestModel.extend({
} }
return ""; return "";
}, }
@discourseComputed( @discourseComputed(
"draftSaving", "draftSaving",
@ -1219,7 +1273,7 @@ const Composer = RestModel.extend({
} }
return true; return true;
}, }
saveDraft(user) { saveDraft(user) {
if (!this.canSaveDraft) { if (!this.canSaveDraft) {
@ -1308,7 +1362,7 @@ const Composer = RestModel.extend({
.finally(() => { .finally(() => {
this.set("draftSaving", false); this.set("draftSaving", false);
}); });
}, }
customizationFor(type) { customizationFor(type) {
for (let i = 0; i < _customizations.length; i++) { for (let i = 0; i < _customizations.length; i++) {
@ -1320,77 +1374,5 @@ const Composer = RestModel.extend({
} }
} }
} }
},
});
Composer.reopenClass({
// TODO: Replace with injection
create(args) {
args = args || {};
args.user = args.user || User.current();
args.site = args.site || Site.current();
return this._super(args);
},
serializeToTopic(fieldName, property) {
if (!property) {
property = fieldName;
} }
_edit_topic_serializer[fieldName] = property; }
},
serializeOnCreate(fieldName, property) {
if (!property) {
property = fieldName;
}
_create_serializer[fieldName] = property;
},
serializedFieldsForCreate() {
return Object.keys(_create_serializer);
},
serializeOnUpdate(fieldName, property) {
if (!property) {
property = fieldName;
}
_update_serializer[fieldName] = property;
},
serializedFieldsForUpdate() {
return Object.keys(_update_serializer);
},
serializeToDraft(fieldName, property) {
if (!property) {
property = fieldName;
}
_draft_serializer[fieldName] = property;
_add_draft_fields[fieldName] = property;
},
serializedFieldsForDraft() {
return Object.keys(_draft_serializer);
},
// The status the compose view can have
CLOSED,
SAVING,
OPEN,
DRAFT,
FULLSCREEN,
// The actions the composer can take
CREATE_TOPIC,
CREATE_SHARED_DRAFT,
EDIT_SHARED_DRAFT,
PRIVATE_MESSAGE,
REPLY,
EDIT,
// Draft key
NEW_PRIVATE_MESSAGE_KEY,
NEW_TOPIC_KEY,
});
export default Composer;