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