diff --git a/app/assets/javascripts/discourse/app/components/category-list-item.js b/app/assets/javascripts/discourse/app/components/category-list-item.js index eaef68762f2..2aef3d62ce4 100644 --- a/app/assets/javascripts/discourse/app/components/category-list-item.js +++ b/app/assets/javascripts/discourse/app/components/category-list-item.js @@ -1,4 +1,5 @@ import Component from "@ember/component"; +import { tagName } from "@ember-decorators/component"; import discourseComputed from "discourse-common/utils/decorators"; const LIST_TYPE = { @@ -6,10 +7,10 @@ const LIST_TYPE = { MUTED: "muted", }; -export default Component.extend({ - tagName: "", - category: null, - listType: LIST_TYPE.NORMAL, +@tagName("") +export default class CategoryListItem extends Component { + category = null; + listType = LIST_TYPE.NORMAL; @discourseComputed("category.isHidden", "category.hasMuted", "listType") isHidden(isHiddenCategory, hasMuted, listType) { @@ -17,7 +18,7 @@ export default Component.extend({ (isHiddenCategory && listType === LIST_TYPE.NORMAL) || (!hasMuted && listType === LIST_TYPE.MUTED) ); - }, + } @discourseComputed("category.isMuted", "listType") isMuted(isMutedCategory, listType) { @@ -25,20 +26,20 @@ export default Component.extend({ (isMutedCategory && listType === LIST_TYPE.NORMAL) || (!isMutedCategory && listType === LIST_TYPE.MUTED) ); - }, + } @discourseComputed("topicTrackingState.messageCount") unreadTopicsCount() { return this.category.unreadTopicsCount; - }, + } @discourseComputed("topicTrackingState.messageCount") newTopicsCount() { return this.category.newTopicsCount; - }, + } @discourseComputed("category.path") slugPath(categoryPath) { return categoryPath.substring("/c/".length); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/category-name-fields.js b/app/assets/javascripts/discourse/app/components/category-name-fields.js index 87d5ddb040f..edc8b634fd0 100644 --- a/app/assets/javascripts/discourse/app/components/category-name-fields.js +++ b/app/assets/javascripts/discourse/app/components/category-name-fields.js @@ -1,3 +1,3 @@ import Component from "@ember/component"; -export default Component.extend({}); +export default class CategoryNameFields extends Component {} diff --git a/app/assets/javascripts/discourse/app/components/category-permission-row.js b/app/assets/javascripts/discourse/app/components/category-permission-row.js index b7a80a193e3..aa2d0a6b271 100644 --- a/app/assets/javascripts/discourse/app/components/category-permission-row.js +++ b/app/assets/javascripts/discourse/app/components/category-permission-row.js @@ -1,43 +1,45 @@ import Component from "@ember/component"; import { action } from "@ember/object"; import { alias, equal } from "@ember/object/computed"; +import { classNames } from "@ember-decorators/component"; +import { observes } from "@ember-decorators/object"; import PermissionType from "discourse/models/permission-type"; -import discourseComputed, { observes } from "discourse-common/utils/decorators"; +import discourseComputed from "discourse-common/utils/decorators"; import I18n from "discourse-i18n"; const EVERYONE = "everyone"; -export default Component.extend({ - classNames: ["permission-row", "row-body"], - canCreate: equal("type", PermissionType.FULL), - everyonePermissionType: alias("everyonePermission.permission_type"), +@classNames("permission-row", "row-body") +export default class CategoryPermissionRow extends Component { + @equal("type", PermissionType.FULL) canCreate; + @alias("everyonePermission.permission_type") everyonePermissionType; @discourseComputed("type") canReply(value) { return ( value === PermissionType.CREATE_POST || value === PermissionType.FULL ); - }, + } @discourseComputed("type") canReplyIcon() { return this.canReply ? "check-square" : "far-square"; - }, + } @discourseComputed("type") canCreateIcon() { return this.canCreate ? "check-square" : "far-square"; - }, + } @discourseComputed("type") replyGranted() { return this.type <= PermissionType.CREATE_POST ? "reply-granted" : ""; - }, + } @discourseComputed("type") createGranted() { return this.type === PermissionType.FULL ? "create-granted" : ""; - }, + } @observes("everyonePermissionType") inheritFromEveryone() { @@ -49,7 +51,7 @@ export default Component.extend({ if (this.everyonePermissionType < this.type) { this.updatePermission(this.everyonePermissionType); } - }, + } @discourseComputed("everyonePermissionType", "type") replyDisabled(everyonePermissionType) { @@ -61,14 +63,14 @@ export default Component.extend({ return true; } return false; - }, + } @discourseComputed("replyDisabled") replyTooltip(replyDisabled) { return replyDisabled ? I18n.t("category.permissions.inherited") : I18n.t("category.permissions.toggle_reply"); - }, + } @discourseComputed("everyonePermissionType", "type") createDisabled(everyonePermissionType) { @@ -80,47 +82,47 @@ export default Component.extend({ return true; } return false; - }, + } @discourseComputed("createDisabled") createTooltip(createDisabled) { return createDisabled ? I18n.t("category.permissions.inherited") : I18n.t("category.permissions.toggle_full"); - }, + } updatePermission(type) { this.category.updatePermission(this.group_name, type); - }, + } @action removeRow(event) { event?.preventDefault(); this.category.removePermission(this.group_name); - }, + } - actions: { - setPermissionReply() { - if (this.type <= PermissionType.CREATE_POST) { - this.updatePermission(PermissionType.READONLY); - } else { - this.updatePermission(PermissionType.CREATE_POST); - } - }, + @action + setPermissionReply() { + if (this.type <= PermissionType.CREATE_POST) { + this.updatePermission(PermissionType.READONLY); + } else { + this.updatePermission(PermissionType.CREATE_POST); + } + } - setPermissionFull() { - if ( - this.group_name !== EVERYONE && - this.everyonePermissionType === PermissionType.FULL - ) { - return; - } + @action + setPermissionFull() { + if ( + this.group_name !== EVERYONE && + this.everyonePermissionType === PermissionType.FULL + ) { + return; + } - if (this.type === PermissionType.FULL) { - this.updatePermission(PermissionType.CREATE_POST); - } else { - this.updatePermission(PermissionType.FULL); - } - }, - }, -}); + if (this.type === PermissionType.FULL) { + this.updatePermission(PermissionType.CREATE_POST); + } else { + this.updatePermission(PermissionType.FULL); + } + } +} diff --git a/app/assets/javascripts/discourse/app/components/category-read-only-banner.js b/app/assets/javascripts/discourse/app/components/category-read-only-banner.js index d8371808f27..495409e4780 100644 --- a/app/assets/javascripts/discourse/app/components/category-read-only-banner.js +++ b/app/assets/javascripts/discourse/app/components/category-read-only-banner.js @@ -2,10 +2,11 @@ import Component from "@ember/component"; import { and } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ +export default class CategoryReadOnlyBanner extends Component { + @and("category.read_only_banner", "readOnly", "user") shouldShow; + @discourseComputed user() { return this.currentUser; - }, - shouldShow: and("category.read_only_banner", "readOnly", "user"), -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/category-title-before.js b/app/assets/javascripts/discourse/app/components/category-title-before.js index 0e6d50b17d4..60ba901dff1 100644 --- a/app/assets/javascripts/discourse/app/components/category-title-before.js +++ b/app/assets/javascripts/discourse/app/components/category-title-before.js @@ -1,4 +1,5 @@ import Component from "@ember/component"; -export default Component.extend({ - tagName: "", -}); +import { tagName } from "@ember-decorators/component"; + +@tagName("") +export default class CategoryTitleBefore extends Component {} diff --git a/app/assets/javascripts/discourse/app/components/category-title-link.js b/app/assets/javascripts/discourse/app/components/category-title-link.js index 1ac2a2fbab3..a5120584b6e 100644 --- a/app/assets/javascripts/discourse/app/components/category-title-link.js +++ b/app/assets/javascripts/discourse/app/components/category-title-link.js @@ -1,6 +1,8 @@ import Component from "@ember/component"; -export default Component.extend({ - tagName: "h3", - // icon name defined here so it can be easily overridden in theme components - lockIcon: "lock", -}); +import { tagName } from "@ember-decorators/component"; + +@tagName("h3") +export default class CategoryTitleLink extends Component {} + +// icon name defined on prototype so it can be easily overridden in theme components +CategoryTitleLink.prototype.lockIcon = "lock"; diff --git a/app/assets/javascripts/discourse/app/components/category-unread.js b/app/assets/javascripts/discourse/app/components/category-unread.js index 4719d63b642..33d4e31ad81 100644 --- a/app/assets/javascripts/discourse/app/components/category-unread.js +++ b/app/assets/javascripts/discourse/app/components/category-unread.js @@ -1,5 +1,6 @@ import Component from "@ember/component"; -export default Component.extend({ - tagName: "span", - classNames: ["category__badges"], -}); +import { classNames, tagName } from "@ember-decorators/component"; + +@tagName("span") +@classNames("category__badges") +export default class CategoryUnread extends Component {} diff --git a/app/assets/javascripts/discourse/app/components/choose-message.js b/app/assets/javascripts/discourse/app/components/choose-message.js index c91a2dd8f4b..d8f00087a4e 100644 --- a/app/assets/javascripts/discourse/app/components/choose-message.js +++ b/app/assets/javascripts/discourse/app/components/choose-message.js @@ -2,14 +2,15 @@ import Component from "@ember/component"; import { action, get } from "@ember/object"; import { next } from "@ember/runloop"; import { isEmpty } from "@ember/utils"; +import { observes } from "@ember-decorators/object"; import $ from "jquery"; import { searchForTerm } from "discourse/lib/search"; -import { debounce, observes } from "discourse-common/utils/decorators"; +import { debounce } from "discourse-common/utils/decorators"; -export default Component.extend({ - loading: null, - noResults: null, - messages: null, +export default class ChooseMessage extends Component { + loading = null; + noResults = null; + messages = null; @observes("messageTitle") messageTitleChanged() { @@ -19,7 +20,7 @@ export default Component.extend({ selectedTopicId: null, }); this.search(this.messageTitle); - }, + } @observes("messages") messagesChanged() { @@ -28,7 +29,7 @@ export default Component.extend({ this.set("noResults", messages.length === 0); } this.set("loading", false); - }, + } @debounce(300) search(title) { @@ -53,7 +54,7 @@ export default Component.extend({ this.setProperties({ messages: null, loading: false }); } }); - }, + } @action chooseMessage(message, event) { @@ -61,5 +62,5 @@ export default Component.extend({ const messageId = get(message, "id"); this.set("selectedTopicId", messageId); next(() => $(`#choose-message-${messageId}`).prop("checked", "true")); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/color-picker-choice.js b/app/assets/javascripts/discourse/app/components/color-picker-choice.js index 36a0bfd441a..e3fba98d10e 100644 --- a/app/assets/javascripts/discourse/app/components/color-picker-choice.js +++ b/app/assets/javascripts/discourse/app/components/color-picker-choice.js @@ -1,30 +1,34 @@ import Component from "@ember/component"; import { htmlSafe } from "@ember/template"; +import { + attributeBindings, + classNameBindings, + tagName, +} from "@ember-decorators/component"; import discourseComputed from "discourse-common/utils/decorators"; import I18n from "discourse-i18n"; -export default Component.extend({ - tagName: "button", - attributeBindings: ["style", "title"], - classNameBindings: [":colorpicker", "isUsed:used-color:unused-color"], - +@tagName("button") +@attributeBindings("style", "title") +@classNameBindings(":colorpicker", "isUsed:used-color:unused-color") +export default class ColorPickerChoice extends Component { @discourseComputed("color", "usedColors") isUsed(color, usedColors) { return (usedColors || []).includes(color.toUpperCase()); - }, + } @discourseComputed("isUsed") title(isUsed) { return isUsed ? I18n.t("category.already_used") : null; - }, + } @discourseComputed("color") style(color) { return htmlSafe(`background-color: #${color};`); - }, + } click(e) { e.preventDefault(); this.selectColor(this.color); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/color-picker.js b/app/assets/javascripts/discourse/app/components/color-picker.js index 22ff988c837..a61baf746ee 100644 --- a/app/assets/javascripts/discourse/app/components/color-picker.js +++ b/app/assets/javascripts/discourse/app/components/color-picker.js @@ -1,10 +1,11 @@ import Component from "@ember/component"; -export default Component.extend({ - classNames: "colors-container", +import { action } from "@ember/object"; +import { classNames } from "@ember-decorators/component"; - actions: { - selectColor(color) { - this.set("value", color); - }, - }, -}); +@classNames("colors-container") +export default class ColorPicker extends Component { + @action + selectColor(color) { + this.set("value", color); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-action-title.js b/app/assets/javascripts/discourse/app/components/composer-action-title.js index 84590d2ecac..a72c4607fa9 100644 --- a/app/assets/javascripts/discourse/app/components/composer-action-title.js +++ b/app/assets/javascripts/discourse/app/components/composer-action-title.js @@ -1,6 +1,7 @@ import Component from "@ember/component"; import { alias } from "@ember/object/computed"; import { htmlSafe } from "@ember/template"; +import { classNames } from "@ember-decorators/component"; import { CREATE_SHARED_DRAFT, CREATE_TOPIC, @@ -21,10 +22,10 @@ const TITLES = { [EDIT_SHARED_DRAFT]: "composer.edit_shared_draft", }; -export default Component.extend({ - classNames: ["composer-action-title"], - options: alias("model.replyOptions"), - action: alias("model.action"), +@classNames("composer-action-title") +export default class ComposerActionTitle extends Component { + @alias("model.replyOptions") options; + @alias("model.action") action; // Note we update when some other attributes like tag/category change to allow // text customizations to use those. @@ -57,7 +58,7 @@ export default Component.extend({ ); } } - }, + } _formatEditUserPost(userAvatar, userLink, postLink, originalUser) { let editTitle = ` @@ -75,7 +76,7 @@ export default Component.extend({ } return htmlSafe(editTitle); - }, + } _formatReplyToTopic(link) { return htmlSafe( @@ -83,12 +84,12 @@ export default Component.extend({ "model.topic.id" )}">${link.anchor}` ); - }, + } _formatReplyToUserPost(avatar, link) { const htmlLink = `${escape( link.anchor )}`; return htmlSafe(`${avatar}${htmlLink}`); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-body.js b/app/assets/javascripts/discourse/app/components/composer-body.js index 120c5803a72..af2bca42844 100644 --- a/app/assets/javascripts/discourse/app/components/composer-body.js +++ b/app/assets/javascripts/discourse/app/components/composer-body.js @@ -1,15 +1,14 @@ import Component from "@ember/component"; import { cancel, schedule, throttle } from "@ember/runloop"; +import { classNameBindings } from "@ember-decorators/component"; +import { observes } from "@ember-decorators/object"; import { headerOffset } from "discourse/lib/offset-calculator"; import positioningWorkaround from "discourse/lib/safari-hacks"; import { isiPad } from "discourse/lib/utilities"; import Composer from "discourse/models/composer"; import discourseDebounce from "discourse-common/lib/debounce"; import discourseLater from "discourse-common/lib/later"; -import discourseComputed, { - bind, - observes, -} from "discourse-common/utils/decorators"; +import discourseComputed, { bind } from "discourse-common/utils/decorators"; const START_DRAG_EVENTS = ["touchstart", "mousedown"]; const DRAG_EVENTS = ["touchmove", "mousemove"]; @@ -21,37 +20,36 @@ function mouseYPos(e) { return e.clientY || (e.touches && e.touches[0] && e.touches[0].clientY); } -export default Component.extend({ - elementId: "reply-control", - - classNameBindings: [ - "composer.creatingPrivateMessage:private-message", - "composeState", - "composer.loading", - "prefixedComposerAction", - "composer.canEditTitle:edit-title", - "composer.createdPost:created-post", - "composer.creatingTopic:topic", - "composer.whisper:composing-whisper", - "composer.sharedDraft:composing-shared-draft", - "showPreview:show-preview:hide-preview", - "currentUserPrimaryGroupClass", - ], +@classNameBindings( + "composer.creatingPrivateMessage:private-message", + "composeState", + "composer.loading", + "prefixedComposerAction", + "composer.canEditTitle:edit-title", + "composer.createdPost:created-post", + "composer.creatingTopic:topic", + "composer.whisper:composing-whisper", + "composer.sharedDraft:composing-shared-draft", + "showPreview:show-preview:hide-preview", + "currentUserPrimaryGroupClass" +) +export default class ComposerBody extends Component { + elementId = "reply-control"; @discourseComputed("composer.action") prefixedComposerAction(action) { return action ? `composer-action-${action}` : ""; - }, + } @discourseComputed("currentUser.primary_group_name") currentUserPrimaryGroupClass(primaryGroupName) { return primaryGroupName && `group-${primaryGroupName}`; - }, + } @discourseComputed("composer.composeState") composeState(composeState) { return composeState || Composer.CLOSED; - }, + } keyUp() { this.typed(); @@ -68,14 +66,14 @@ export default Component.extend({ } this.appEvents.trigger("composer:find-similar"); }, 1000); - }, + } @observes("composeState") disableFullscreen() { if (this.composeState !== Composer.OPEN && positioningWorkaround.blur) { positioningWorkaround.blur(); } - }, + } setupComposerResizeEvents() { this.origComposerSize = 0; @@ -88,7 +86,7 @@ export default Component.extend({ passive: false, }); }); - }, + } @bind performDragHandler() { @@ -112,14 +110,14 @@ export default Component.extend({ ); this._triggerComposerResized(); - }, + } @observes("composeState", "composer.{action,canEditTopicFeaturedLink}") _triggerComposerResized() { schedule("afterRender", () => { discourseDebounce(this, this.composerResized, 300); }); - }, + } composerResized() { if (!this.element || this.isDestroying || this.isDestroyed) { @@ -127,7 +125,7 @@ export default Component.extend({ } this.appEvents.trigger("composer:resized"); - }, + } @bind startDragHandler(event) { @@ -145,7 +143,7 @@ export default Component.extend({ }); this.appEvents.trigger("composer:resize-started"); - }, + } @bind endDragHandler() { @@ -161,16 +159,16 @@ export default Component.extend({ this.element.classList.remove("clear-transitions"); this.element.focus(); - }, + } @bind throttledPerformDrag(event) { event.preventDefault(); throttle(this, this.performDragHandler, event, THROTTLE_RATE); - }, + } didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); this.setupComposerResizeEvents(); @@ -188,10 +186,10 @@ export default Component.extend({ }); positioningWorkaround(this.element); - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); START_DRAG_EVENTS.forEach((startDragEvent) => { this.element @@ -200,11 +198,11 @@ export default Component.extend({ }); cancel(this._lastKeyTimeout); - }, + } click() { this.openIfDraft(); - }, + } keyDown(e) { if (e.key === "Escape") { @@ -220,5 +218,5 @@ export default Component.extend({ e.preventDefault(); this.save(undefined, e); } - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.hbs b/app/assets/javascripts/discourse/app/components/composer-editor.hbs index 33672a3afa9..60ceaebc44b 100644 --- a/app/assets/javascripts/discourse/app/components/composer-editor.hbs +++ b/app/assets/javascripts/discourse/app/components/composer-editor.hbs @@ -4,9 +4,9 @@ @previewUpdated={{action "previewUpdated"}} @markdownOptions={{this.markdownOptions}} @extraButtons={{action "extraButtons"}} - @importQuote={{action "importQuote"}} + @importQuote={{this.importQuote}} @showUploadModal={{this.showUploadModal}} - @togglePreview={{action "togglePreview"}} + @togglePreview={{this.togglePreview}} @processPreview={{this.processPreview}} @validation={{this.validation}} @loading={{this.composer.loading}} diff --git a/app/assets/javascripts/discourse/app/components/composer-editor.js b/app/assets/javascripts/discourse/app/components/composer-editor.js index 65a3819f24d..c30bfc29239 100644 --- a/app/assets/javascripts/discourse/app/components/composer-editor.js +++ b/app/assets/javascripts/discourse/app/components/composer-editor.js @@ -1,8 +1,10 @@ import Component from "@ember/component"; -import EmberObject, { computed } from "@ember/object"; +import EmberObject, { action, computed } from "@ember/object"; import { alias } from "@ember/object/computed"; import { getOwner } from "@ember/owner"; import { next, schedule, throttle } from "@ember/runloop"; +import { classNameBindings } from "@ember-decorators/component"; +import { observes } from "@ember-decorators/object"; import { BasePlugin } from "@uppy/core"; import $ from "jquery"; import { resolveAllShortUrls } from "pretty-text/upload-short-url"; @@ -39,7 +41,6 @@ import { findRawTemplate } from "discourse-common/lib/raw-templates"; import discourseComputed, { bind, debounce, - observes, on, } from "discourse-common/utils/decorators"; import I18n from "discourse-i18n"; @@ -109,32 +110,32 @@ export function addApiImageWrapperButtonClickEvent(fn) { const DEBOUNCE_FETCH_MS = 450; const DEBOUNCE_JIT_MS = 2000; -export default Component.extend(ComposerUploadUppy, { - classNameBindings: ["showToolbar:toolbar-visible", ":wmd-controls"], +@classNameBindings("showToolbar:toolbar-visible", ":wmd-controls") +export default class ComposerEditor extends Component.extend( + ComposerUploadUppy +) { + editorClass = ".d-editor"; + fileUploadElementId = "file-uploader"; + mobileFileUploaderId = "mobile-file-upload"; + composerEventPrefix = "composer"; + uploadType = "composer"; + uppyId = "composer-editor-uppy"; + composerModelContentKey = "reply"; + editorInputClass = ".d-editor-input"; + shouldBuildScrollMap = true; + scrollMap = null; + processPreview = true; + uploadMarkdownResolvers = uploadMarkdownResolvers; + uploadPreProcessors = uploadPreProcessors; + uploadHandlers = uploadHandlers; - editorClass: ".d-editor", - fileUploadElementId: "file-uploader", - mobileFileUploaderId: "mobile-file-upload", - - composerEventPrefix: "composer", - uploadType: "composer", - uppyId: "composer-editor-uppy", - composerModel: alias("composer"), - composerModelContentKey: "reply", - editorInputClass: ".d-editor-input", - shouldBuildScrollMap: true, - scrollMap: null, - processPreview: true, - - uploadMarkdownResolvers, - uploadPreProcessors, - uploadHandlers, + @alias("composer") composerModel; init() { - this._super(...arguments); + super.init(...arguments); this.warnedCannotSeeMentions = []; this.warnedGroupMentions = []; - }, + } @discourseComputed("composer.requiredCategoryMissing") replyPlaceholder(requiredCategoryMissing) { @@ -149,19 +150,19 @@ export default Component.extend(ComposerUploadUppy, { : "reply_placeholder_no_images"; return `composer.${key}`; } - }, + } @discourseComputed showLink() { return this.currentUser && this.currentUser.link_posting_access !== "none"; - }, + } @observes("focusTarget") setFocus() { if (this.focusTarget === "editor") { putCursorAtEnd(this.element.querySelector("textarea")); } - }, + } @discourseComputed markdownOptions() { @@ -204,7 +205,7 @@ export default Component.extend(ComposerUploadUppy, { this.site.hashtag_configurations["topic-composer"], hashtagIcons: this.site.hashtag_icons, }; - }, + } @bind _afterMentionComplete(value) { @@ -216,7 +217,7 @@ export default Component.extend(ComposerUploadUppy, { input?.blur(); input?.focus(); }); - }, + } @on("didInsertElement") _composerEditorInit() { @@ -266,7 +267,7 @@ export default Component.extend(ComposerUploadUppy, { } this.appEvents.trigger(`${this.composerEventPrefix}:will-open`); - }, + } @discourseComputed( "composer.reply", @@ -313,7 +314,7 @@ export default Component.extend(ComposerUploadUppy, { lastShownAt: lastValidatedAt, }); } - }, + } @computed("composer.{creatingTopic,editingFirstPost,creatingSharedDraft}") get _isNewTopic() { @@ -322,11 +323,11 @@ export default Component.extend(ComposerUploadUppy, { this.composer.editingFirstPost || this.composer.creatingSharedDraft ); - }, + } _resetShouldBuildScrollMap() { this.set("shouldBuildScrollMap", true); - }, + } @bind _handleInputInteraction(event) { @@ -338,7 +339,7 @@ export default Component.extend(ComposerUploadUppy, { preview.removeEventListener("scroll", this._handleInputOrPreviewScroll); event.target.addEventListener("scroll", this._handleInputOrPreviewScroll); - }, + } @bind _handleInputOrPreviewScroll(event) { @@ -347,7 +348,7 @@ export default Component.extend(ComposerUploadUppy, { $(event.target), $(this.element.querySelector(".d-editor-preview-wrapper")) ); - }, + } @bind _handlePreviewInteraction(event) { @@ -356,7 +357,7 @@ export default Component.extend(ComposerUploadUppy, { ?.removeEventListener("scroll", this._handleInputOrPreviewScroll); event.target?.addEventListener("scroll", this._handleInputOrPreviewScroll); - }, + } _syncScroll($callback, $input, $preview) { if (!this.scrollMap || this.shouldBuildScrollMap) { @@ -365,7 +366,7 @@ export default Component.extend(ComposerUploadUppy, { } throttle(this, $callback, $input, $preview, this.scrollMap, 20); - }, + } // Adapted from https://github.com/markdown-it/markdown-it.github.io _buildScrollMap($input, $preview) { @@ -452,7 +453,7 @@ export default Component.extend(ComposerUploadUppy, { } return scrollMap; - }, + } @bind _throttledSyncEditorAndPreviewScroll(event) { @@ -465,7 +466,7 @@ export default Component.extend(ComposerUploadUppy, { $preview, 20 ); - }, + } _syncEditorAndPreviewScroll($input, $preview) { if (!$input) { @@ -490,7 +491,7 @@ export default Component.extend(ComposerUploadUppy, { const factor = previewHeight / inputHeight; const desired = scrollPosition * factor; $preview.scrollTop(desired + 50); - }, + } _renderMentions(preview, unseen) { unseen ||= linkSeenMentions(preview, this.siteSettings); @@ -500,7 +501,7 @@ export default Component.extend(ComposerUploadUppy, { this._warnMentionedGroups(preview); this._warnCannotSeeMention(preview); } - }, + } @debounce(DEBOUNCE_FETCH_MS) _renderUnseenMentions(preview, unseen) { @@ -514,7 +515,7 @@ export default Component.extend(ComposerUploadUppy, { this._warnCannotSeeMention(preview); this._warnHereMention(response.here_count); }); - }, + } _renderHashtags(preview, unseen) { const context = this.site.hashtag_configurations["topic-composer"]; @@ -522,14 +523,14 @@ export default Component.extend(ComposerUploadUppy, { if (unseen.length > 0) { this._renderUnseenHashtags(preview, unseen, context); } - }, + } @debounce(DEBOUNCE_FETCH_MS) _renderUnseenHashtags(preview, unseen, context) { fetchUnseenHashtagsInContext(context, unseen).then(() => linkSeenHashtagsInContext(context, preview) ); - }, + } @debounce(DEBOUNCE_FETCH_MS) _refreshOneboxes(preview) { @@ -549,15 +550,15 @@ export default Component.extend(ComposerUploadUppy, { if (refresh && loaded > 0) { post.set("refreshedPost", true); } - }, + } _expandShortUrls(preview) { resolveAllShortUrls(ajax, this.siteSettings, preview); - }, + } _decorateCookedElement(preview) { this.appEvents.trigger("decorate-non-stream-cooked-element", preview); - }, + } @debounce(DEBOUNCE_JIT_MS) _warnMentionedGroups(preview) { @@ -581,7 +582,7 @@ export default Component.extend(ComposerUploadUppy, { }); }); }); - }, + } // add a delay to allow for typing, so you don't open the warning right away // previously we would warn after @bob even if you were about to mention @bob2 @@ -620,7 +621,7 @@ export default Component.extend(ComposerUploadUppy, { isGroup: true, }); }); - }, + } _warnHereMention(hereCount) { if (!hereCount || hereCount === 0) { @@ -628,7 +629,7 @@ export default Component.extend(ComposerUploadUppy, { } this.hereMention(hereCount); - }, + } @bind _handleImageScaleButtonClick(event) { @@ -665,7 +666,7 @@ export default Component.extend(ComposerUploadUppy, { event.preventDefault(); return; - }, + } resetImageControls(buttonWrapper) { const imageResize = buttonWrapper.querySelector(".scale-btn-container"); @@ -684,7 +685,7 @@ export default Component.extend(ComposerUploadUppy, { readonlyContainer.removeAttribute("hidden"); buttonWrapper.removeAttribute("editing"); editContainer.setAttribute("hidden", "true"); - }, + } commitAltText(buttonWrapper) { const index = parseInt(buttonWrapper.getAttribute("data-image-index"), 10); @@ -704,7 +705,7 @@ export default Component.extend(ComposerUploadUppy, { ); this.resetImageControls(buttonWrapper); - }, + } @bind _handleAltTextInputKeypress(event) { @@ -720,7 +721,7 @@ export default Component.extend(ComposerUploadUppy, { const buttonWrapper = event.target.closest(".button-wrapper"); this.commitAltText(buttonWrapper); } - }, + } @bind _handleAltTextEditButtonClick(event) { @@ -750,7 +751,7 @@ export default Component.extend(ComposerUploadUppy, { editContainer.removeAttribute("hidden"); editContainerInput.focus(); event.preventDefault(); - }, + } @bind _handleAltTextOkButtonClick(event) { @@ -760,7 +761,7 @@ export default Component.extend(ComposerUploadUppy, { const buttonWrapper = event.target.closest(".button-wrapper"); this.commitAltText(buttonWrapper); - }, + } @bind _handleAltTextCancelButtonClick(event) { @@ -770,7 +771,7 @@ export default Component.extend(ComposerUploadUppy, { const buttonWrapper = event.target.closest(".button-wrapper"); this.resetImageControls(buttonWrapper); - }, + } @bind _handleImageDeleteButtonClick(event) { @@ -789,7 +790,7 @@ export default Component.extend(ComposerUploadUppy, { "", { regex: IMAGE_MARKDOWN_REGEX, index } ); - }, + } @bind _handleImageGridButtonClick(event) { @@ -818,7 +819,7 @@ export default Component.extend(ComposerUploadUppy, { "grid_surround", { useBlockMode: true } ); - }, + } _registerImageAltTextButtonClick(preview) { preview.addEventListener("click", this._handleAltTextCancelButtonClick); @@ -832,7 +833,7 @@ export default Component.extend(ComposerUploadUppy, { apiImageWrapperBtnEvents.forEach((fn) => preview.addEventListener("click", fn) ); - }, + } @on("willDestroyElement") _composerClosed() { @@ -870,17 +871,18 @@ export default Component.extend(ComposerUploadUppy, { apiImageWrapperBtnEvents.forEach((fn) => preview?.removeEventListener("click", fn) ); - }, + } + @action onExpandPopupMenuOptions(toolbarEvent) { const selected = toolbarEvent.selected; toolbarEvent.selectText(selected.start, selected.end - selected.start); this.storeToolbarState(toolbarEvent); - }, + } showPreview() { this.send("togglePreview"); - }, + } _isInQuote(element) { let parent = element.parentElement; @@ -893,18 +895,18 @@ export default Component.extend(ComposerUploadUppy, { } return false; - }, + } _isPreviewRoot(element) { return ( element.tagName === "DIV" && element.classList.contains("d-editor-preview") ); - }, + } _isQuote(element) { return element.tagName === "ASIDE" && element.classList.contains("quote"); - }, + } _cursorIsOnEmptyLine() { const textArea = this.element.querySelector(".d-editor-input"); @@ -916,7 +918,7 @@ export default Component.extend(ComposerUploadUppy, { } else { return false; } - }, + } _findMatchingUploadHandler(fileName) { return this.uploadHandlers.find((handler) => { @@ -924,62 +926,50 @@ export default Component.extend(ComposerUploadUppy, { const regex = new RegExp(`\\.(${ext})$`, "i"); return regex.test(fileName); }); - }, + } - actions: { - importQuote(toolbarEvent) { - this.importQuote(toolbarEvent); - }, + @action + extraButtons(toolbar) { + toolbar.addButton({ + id: "quote", + group: "fontStyles", + icon: "far-comment", + sendAction: this.importQuote, + title: "composer.quote_post_title", + unshift: true, + }); - onExpandPopupMenuOptions(toolbarEvent) { - this.onExpandPopupMenuOptions(toolbarEvent); - }, - - togglePreview() { - this.togglePreview(); - }, - - extraButtons(toolbar) { + if (this.allowUpload && this.uploadIcon && this.site.desktopView) { toolbar.addButton({ - id: "quote", - group: "fontStyles", - icon: "far-comment", - sendAction: this.importQuote, - title: "composer.quote_post_title", - unshift: true, + id: "upload", + group: "insertions", + icon: this.uploadIcon, + title: "upload", + sendAction: this.showUploadModal, }); + } - if (this.allowUpload && this.uploadIcon && this.site.desktopView) { - toolbar.addButton({ - id: "upload", - group: "insertions", - icon: this.uploadIcon, - title: "upload", - sendAction: this.showUploadModal, - }); - } + toolbar.addButton({ + id: "options", + group: "extras", + icon: "cog", + title: "composer.options", + sendAction: this.onExpandPopupMenuOptions.bind(this), + popupMenu: true, + }); + } - toolbar.addButton({ - id: "options", - group: "extras", - icon: "cog", - title: "composer.options", - sendAction: this.onExpandPopupMenuOptions.bind(this), - popupMenu: true, - }); - }, + @action + previewUpdated(preview, unseenMentions, unseenHashtags) { + this._renderMentions(preview, unseenMentions); + this._renderHashtags(preview, unseenHashtags); + this._refreshOneboxes(preview); + this._expandShortUrls(preview); - previewUpdated(preview, unseenMentions, unseenHashtags) { - this._renderMentions(preview, unseenMentions); - this._renderHashtags(preview, unseenHashtags); - this._refreshOneboxes(preview); - this._expandShortUrls(preview); + if (!this.siteSettings.enable_diffhtml_preview) { + this._decorateCookedElement(preview); + } - if (!this.siteSettings.enable_diffhtml_preview) { - this._decorateCookedElement(preview); - } - - this.afterRefresh(preview); - }, - }, -}); + this.afterRefresh(preview); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-message.js b/app/assets/javascripts/discourse/app/components/composer-message.js index cf72b63ad0c..db44b73f556 100644 --- a/app/assets/javascripts/discourse/app/components/composer-message.js +++ b/app/assets/javascripts/discourse/app/components/composer-message.js @@ -1,23 +1,12 @@ import Component from "@ember/component"; import { getOwner } from "@ember/owner"; -import deprecated from "discourse-common/lib/deprecated"; +import { classNameBindings } from "@ember-decorators/component"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - classNameBindings: [":composer-popup", "message.extraClass"], - +@classNameBindings(":composer-popup", "message.extraClass") +export default class ComposerMessage extends Component { @discourseComputed("message.templateName") layout(templateName) { return getOwner(this).lookup(`template:composer/${templateName}`); - }, - - actions: { - closeMessage() { - deprecated( - 'You should use `action=(closeMessage message)` instead of `action=(action "closeMessage")`', - { id: "discourse.composer-message.closeMessage" } - ); - this.closeMessage(this.message); - }, - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-title.js b/app/assets/javascripts/discourse/app/components/composer-title.js index 5e88d20a5cb..b874679c0d3 100644 --- a/app/assets/javascripts/discourse/app/components/composer-title.js +++ b/app/assets/javascripts/discourse/app/components/composer-title.js @@ -2,23 +2,26 @@ import Component from "@ember/component"; import EmberObject from "@ember/object"; import { alias, or } from "@ember/object/computed"; import { next, schedule } from "@ember/runloop"; +import { classNames } from "@ember-decorators/component"; +import { observes } from "@ember-decorators/object"; import { load } from "pretty-text/oneboxer"; import { lookupCache } from "pretty-text/oneboxer-cache"; import { ajax } from "discourse/lib/ajax"; import putCursorAtEnd from "discourse/lib/put-cursor-at-end"; import { isTesting } from "discourse-common/config/environment"; import discourseDebounce from "discourse-common/lib/debounce"; -import discourseComputed, { observes } from "discourse-common/utils/decorators"; +import discourseComputed from "discourse-common/utils/decorators"; import I18n from "discourse-i18n"; -export default Component.extend({ - classNames: ["title-input"], - watchForLink: alias("composer.canEditTopicFeaturedLink"), - disabled: or("composer.loading", "composer.disableTitleInput"), - isTitleFocused: false, +@classNames("title-input") +export default class ComposerTitle extends Component { + @alias("composer.canEditTopicFeaturedLink") watchForLink; + @or("composer.loading", "composer.disableTitleInput") disabled; + + isTitleFocused = false; didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); const titleInput = this.element.querySelector("input"); this._focusHandler = () => this.set("isTitleFocused", true); @@ -34,17 +37,17 @@ export default Component.extend({ if (this.get("composer.titleLength") > 0) { discourseDebounce(this, this._titleChanged, 10); } - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); const titleInput = this.element.querySelector("input"); if (titleInput) { titleInput.removeEventListener("focus", this._focusHandler); titleInput.removeEventListener("blur", this._blurHandler); } - }, + } @discourseComputed( "composer.titleLength", @@ -83,14 +86,14 @@ export default Component.extend({ lastShownAt: lastValidatedAt, }); } - }, + } @discourseComputed("watchForLink") titleMaxLength(watchForLink) { // maxLength gets in the way of pasting long links, so don't use it if featured links are allowed. // Validation will display a message if titles are too long. return watchForLink ? null : this.siteSettings.max_topic_title_length; - }, + } @observes("composer.titleLength", "watchForLink") _titleChanged() { @@ -110,14 +113,14 @@ export default Component.extend({ } else { discourseDebounce(this, this._checkForUrl, 500); } - }, + } @observes("composer.replyLength") _clearFeaturedLink() { if (this.watchForLink && this.bodyIsDefault()) { this.set("composer.featuredLink", null); } - }, + } _checkForUrl() { if (!this.element || this.isDestroying || this.isDestroyed) { @@ -169,7 +172,7 @@ export default Component.extend({ }); } } - }, + } _updatePost(html) { if (html) { @@ -207,13 +210,13 @@ export default Component.extend({ } } } - }, + } changeTitle(val) { if (val && val.length > 0) { this.set("composer.title", val.trim()); } - }, + } @discourseComputed("composer.title", "composer.titleLength") isAbsoluteUrl(title, titleLength) { @@ -222,7 +225,7 @@ export default Component.extend({ /^(https?:)?\/\/[\w\.\-]+/i.test(title) && !/\s/.test(title) ); - }, + } bodyIsDefault() { const reply = this.get("composer.reply") || ""; @@ -230,5 +233,5 @@ export default Component.extend({ reply.length === 0 || reply === (this.get("composer.category.topic_template") || "") ); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-toggles.js b/app/assets/javascripts/discourse/app/components/composer-toggles.js index 50fd6547aa7..8462bb8036a 100644 --- a/app/assets/javascripts/discourse/app/components/composer-toggles.js +++ b/app/assets/javascripts/discourse/app/components/composer-toggles.js @@ -1,20 +1,20 @@ import Component from "@ember/component"; +import { tagName } from "@ember-decorators/component"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - tagName: "", - +@tagName("") +export default class ComposerToggles extends Component { @discourseComputed("composeState") toggleTitle(composeState) { return composeState === "draft" || composeState === "saving" ? "composer.abandon" : "composer.collapse"; - }, + } @discourseComputed("showToolbar") toggleToolbarTitle(showToolbar) { return showToolbar ? "composer.hide_toolbar" : "composer.show_toolbar"; - }, + } @discourseComputed("composeState") fullscreenTitle(composeState) { @@ -23,14 +23,14 @@ export default Component.extend({ : composeState === "fullscreen" ? "composer.exit_fullscreen" : "composer.enter_fullscreen"; - }, + } @discourseComputed("composeState") toggleIcon(composeState) { return composeState === "draft" || composeState === "saving" ? "times" : "chevron-down"; - }, + } @discourseComputed("composeState") fullscreenIcon(composeState) { @@ -39,7 +39,7 @@ export default Component.extend({ : composeState === "fullscreen" ? "discourse-compress" : "discourse-expand"; - }, + } @discourseComputed("disableTextarea") showFullScreenButton(disableTextarea) { @@ -47,5 +47,5 @@ export default Component.extend({ return false; } return !disableTextarea; - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/composer-user-selector.js b/app/assets/javascripts/discourse/app/components/composer-user-selector.js index 8bf2d053912..69921636042 100644 --- a/app/assets/javascripts/discourse/app/components/composer-user-selector.js +++ b/app/assets/javascripts/discourse/app/components/composer-user-selector.js @@ -1,19 +1,17 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend({ - init() { - this._super(...arguments); - this.set("_groups", []); - }, +export default class ComposerUserSelector extends Component { + _groups = []; didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); if (this.focusTarget === "usernames") { this.element.querySelector(".select-kit .select-kit-header").focus(); } - }, + } @discourseComputed("recipients") splitRecipients(recipients) { @@ -21,7 +19,7 @@ export default Component.extend({ return recipients; } return recipients ? recipients.split(",").filter(Boolean) : []; - }, + } _updateGroups(selected, newGroups) { const groups = []; @@ -39,13 +37,12 @@ export default Component.extend({ _groups: groups, hasGroups: groups.length > 0, }); - }, + } - actions: { - updateRecipients(selected, content) { - const newGroups = content.filterBy("isGroup").mapBy("id"); - this._updateGroups(selected, newGroups); - this.set("recipients", selected.join(",")); - }, - }, -}); + @action + updateRecipients(selected, content) { + const newGroups = content.filterBy("isGroup").mapBy("id"); + this._updateGroups(selected, newGroups); + this.set("recipients", selected.join(",")); + } +} diff --git a/app/assets/javascripts/discourse/app/components/conditional-loading-section.js b/app/assets/javascripts/discourse/app/components/conditional-loading-section.js index 903bb472881..5b2e1e80e3c 100644 --- a/app/assets/javascripts/discourse/app/components/conditional-loading-section.js +++ b/app/assets/javascripts/discourse/app/components/conditional-loading-section.js @@ -1,11 +1,10 @@ import Component from "@ember/component"; +import { classNameBindings, classNames } from "@ember-decorators/component"; import I18n from "discourse-i18n"; -export default Component.extend({ - classNames: ["conditional-loading-section"], - classNameBindings: ["isLoading"], - - isLoading: false, - - title: I18n.t("conditional_loading_section.loading"), -}); +@classNames("conditional-loading-section") +@classNameBindings("isLoading") +export default class ConditionalLoadingSection extends Component { + isLoading = false; + title = I18n.t("conditional_loading_section.loading"); +} diff --git a/app/assets/javascripts/discourse/app/components/connector-container.js b/app/assets/javascripts/discourse/app/components/connector-container.js index 6c296745780..ecfa43b9e3f 100644 --- a/app/assets/javascripts/discourse/app/components/connector-container.js +++ b/app/assets/javascripts/discourse/app/components/connector-container.js @@ -1,2 +1,3 @@ import Component from "@ember/component"; -export default Component.extend(); + +export default class ConnectorContainer extends Component {} diff --git a/app/assets/javascripts/discourse/app/components/copy-button.js b/app/assets/javascripts/discourse/app/components/copy-button.js index f447629f498..10f6bdc08df 100644 --- a/app/assets/javascripts/discourse/app/components/copy-button.js +++ b/app/assets/javascripts/discourse/app/components/copy-button.js @@ -1,12 +1,13 @@ import Component from "@ember/component"; import { action } from "@ember/object"; +import { tagName } from "@ember-decorators/component"; import discourseDebounce from "discourse-common/lib/debounce"; import { bind } from "discourse-common/utils/decorators"; -export default Component.extend({ - tagName: "", - copyIcon: "copy", - copyClass: "btn-primary", +@tagName("") +export default class CopyButton extends Component { + copyIcon = "copy"; + copyClass = "btn-primary"; @bind _restoreButton() { @@ -16,7 +17,7 @@ export default Component.extend({ this.set("copyIcon", "copy"); this.set("copyClass", "btn-primary"); - }, + } @action copy() { @@ -36,5 +37,5 @@ export default Component.extend({ discourseDebounce(this._restoreButton, 3000); } catch (err) {} - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/create-invite-uploader.js b/app/assets/javascripts/discourse/app/components/create-invite-uploader.js index dea920d2c92..ed4491da6ed 100644 --- a/app/assets/javascripts/discourse/app/components/create-invite-uploader.js +++ b/app/assets/javascripts/discourse/app/components/create-invite-uploader.js @@ -1,38 +1,41 @@ import Component from "@ember/component"; import { action } from "@ember/object"; +import { tagName } from "@ember-decorators/component"; import UppyUploadMixin from "discourse/mixins/uppy-upload"; import discourseComputed from "discourse-common/utils/decorators"; -export default Component.extend(UppyUploadMixin, { - id: "create-invite-uploader", - tagName: "div", - type: "csv", - autoStartUploads: false, - uploadUrl: "/invites/upload_csv", - preventDirectS3Uploads: true, - fileInputSelector: "#csv-file", +@tagName("div") +export default class CreateInviteUploader extends Component.extend( + UppyUploadMixin +) { + id = "create-invite-uploader"; + type = "csv"; + autoStartUploads = false; + uploadUrl = "/invites/upload_csv"; + preventDirectS3Uploads = true; + fileInputSelector = "#csv-file"; validateUploadedFilesOptions() { return { bypassNewUserRestriction: true, csvOnly: true }; - }, + } @discourseComputed("filesAwaitingUpload", "uploading") submitDisabled(filesAwaitingUpload, uploading) { return !filesAwaitingUpload || uploading; - }, + } uploadDone() { this.set("uploaded", true); - }, + } @action startUpload() { this._startUpload(); - }, + } @action setElement(element) { this.set("fileInputEl", element); this._initialize(); - }, -}); + } +} diff --git a/app/assets/javascripts/discourse/app/components/create-topic-button.js b/app/assets/javascripts/discourse/app/components/create-topic-button.js index e424b5ad7a2..11257c0059e 100644 --- a/app/assets/javascripts/discourse/app/components/create-topic-button.js +++ b/app/assets/javascripts/discourse/app/components/create-topic-button.js @@ -1,6 +1,8 @@ import Component from "@ember/component"; -export default Component.extend({ - tagName: "", - label: "topic.create", - btnClass: "btn-default", -}); +import { tagName } from "@ember-decorators/component"; + +@tagName("") +export default class CreateTopicButton extends Component { + label = "topic.create"; + btnClass = "btn-default"; +} diff --git a/app/assets/javascripts/discourse/app/components/custom-html.js b/app/assets/javascripts/discourse/app/components/custom-html.js index 63c0b9873a4..076ac6799b1 100644 --- a/app/assets/javascripts/discourse/app/components/custom-html.js +++ b/app/assets/javascripts/discourse/app/components/custom-html.js @@ -4,11 +4,11 @@ import { hbs } from "ember-cli-htmlbars"; import { getCustomHTML } from "discourse/helpers/custom-html"; import deprecated from "discourse-common/lib/deprecated"; -export default Component.extend({ - triggerAppEvent: null, +export default class CustomHtml extends Component { + triggerAppEvent = null; init() { - this._super(...arguments); + super.init(...arguments); const name = this.name; const html = getCustomHTML(name); @@ -25,19 +25,19 @@ export default Component.extend({ this.set("layout", template); } } - }, + } didInsertElement() { - this._super(...arguments); + super.didInsertElement(...arguments); if (this.triggerAppEvent === "true") { this.appEvents.trigger(`inserted-custom-html:${this.name}`); } - }, + } willDestroyElement() { - this._super(...arguments); + super.willDestroyElement(...arguments); if (this.triggerAppEvent === "true") { this.appEvents.trigger(`destroyed-custom-html:${this.name}`); } - }, -}); + } +}