From ba27ee16376c961c93a4e3854b038a42f9577613 Mon Sep 17 00:00:00 2001 From: Dan Gebhardt Date: Tue, 4 Oct 2022 04:42:46 -0400 Subject: [PATCH] DEV: Remove usage of `{{action}}` modifiers (#18333) This PR enables the [`no-action-modifiers`](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-action-modifiers.md) template lint rule and removes all usages of the `{{action}}` modifier in core. In general, instances of `{{action "x"}}` have been replaced with `{{on "click" (action "x")}}`. In many cases, such as for `a` elements, we also need to prevent default event handling to avoid unwanted side effects. While the `{{action}}` modifier internally calls `event.preventDefault()`, we need to handle these cases more explicitly. For this purpose, this PR also adds the [ember-event-helpers](https://github.com/buschtoens/ember-event-helpers) dependency so we can use the `prevent-default` handler. For instance: ``` Do X ``` Note that `action` has not in general been refactored away as a helper yet. In general, all event handlers should be methods on the corresponding component and referenced directly (e.g. `{{on "click" this.doSomething}}`). However, the `action` helper is used extensively throughout the codebase and often references methods in the `actions` hash on controllers or routes. Thus this refactor will also be extensive and probably deserves a separate PR. Note: This work was done to complement #17767 by minimizing the potential impact of the `action` modifier override, which uses private API and arguably should be replaced with an AST transform. Commits: * Enable `no-action-modifiers` template lint rule * Replace {{action "x"}} with {{on "click" (action "x")}} * Remove unnecessary action helper usage * Remove ctl+click tests for user-menu These tests now break in Chrome when used with addEventListener. As per the comment, they can probably be safely removed. * Prevent default event handlers to avoid unwanted side effects Uses `event.preventDefault()` in event handlers to prevent default event handling. This had been done automatically by the `action` modifier, but is not always desirable or necessary. * Restore UserCardContents#showUser action to avoid regression By keeping the `showUser` action, we can avoid a breaking change for plugins that rely upon it, while not interfering with the `showUser` argument that's been passed. * Revert EditCategoryTab#selectTab -> EditCategoryTab#select Avoid potential breaking change in themes / plugins * Restore GroupCardContents#showGroup action to avoid regression By keeping the `showGroup` action, we can avoid a breaking change for plugins that rely upon it, while not interfering with the `showGroup` argument that's been passed. * Restore SecondFactorAddTotp#showSecondFactorKey action to avoid regression By keeping the `showSecondFactorKey` action, we can avoid a breaking change for plugins that rely upon it, while not interfering with the `showSecondFactorKey` property that's maintained on the controller. * Refactor away from `actions` hash in ChooseMessage component * Modernize EmojiPicker#onCategorySelection usage * Modernize SearchResultEntry#logClick usage * Modernize Discovery::Categories#showInserted usage * Modernize Preferences::Account#resendConfirmationEmail usage * Modernize MultiSelect::SelectedCategory#onSelectedNameClick usage * Favor fn over action in SelectedChoice component * Modernize WizardStep event handlers * Favor fn over action usage in buttons * Restore Login#forgotPassword action to avoid possible regression --- .template-lintrc.js | 1 + .../addon/components/admin-editable-field.js | 14 +- .../addon/components/admin-theme-editor.js | 33 +++-- .../admin/addon/components/ip-lookup.js | 12 +- .../addon/components/themes-list-item.js | 9 +- .../addon/controllers/admin-badges/show.js | 6 + .../addon/controllers/admin-email-bounced.js | 7 + .../controllers/admin-email-preview-digest.js | 12 +- .../addon/controllers/admin-email-rejected.js | 7 + .../admin-logs-screened-ip-addresses.js | 17 ++- .../admin-logs-staff-action-logs.js | 125 ++++++++++-------- .../admin-web-hooks-show-events.js | 33 ++--- .../modals/admin-uploaded-image-list.js | 11 +- .../addon/templates/admin-badges/show.hbs | 4 +- .../components/admin-editable-field.hbs | 4 +- .../components/admin-theme-editor.hbs | 6 +- .../addon/templates/components/ip-lookup.hbs | 2 +- .../templates/components/themes-list-item.hbs | 2 +- .../admin/addon/templates/email-bounced.hbs | 4 +- .../addon/templates/email-preview-digest.hbs | 4 +- .../admin/addon/templates/email-rejected.hbs | 4 +- .../templates/logs/screened-ip-addresses.hbs | 2 +- .../templates/logs/staff-action-logs.hbs | 20 +-- .../modal/admin-uploaded-image-list.hbs | 2 +- .../addon/templates/web-hooks-show-events.hbs | 2 +- .../app/components/categories-only.js | 3 +- .../app/components/category-permission-row.js | 11 +- .../app/components/choose-message.js | 15 +-- .../app/components/composer-messages.js | 12 +- .../app/components/edit-category-tab.js | 16 +-- .../discourse/app/components/emoji-picker.js | 3 +- .../app/components/group-card-contents.js | 23 +++- .../components/group-imap-email-settings.js | 3 +- .../discourse/app/components/group-member.js | 10 +- .../components/group-smtp-email-settings.js | 3 +- .../discourse/app/components/mobile-nav.js | 41 +++--- .../app/components/navigation-bar.js | 43 +++--- .../app/components/reviewable-item.js | 47 ++++--- .../app/components/reviewable-post-edits.js | 25 ++-- .../app/components/reviewable-queued-post.js | 9 +- .../app/components/search-result-entry.js | 1 + .../app/components/second-factor-form.js | 21 +-- .../app/components/security-key-form.js | 13 +- .../discourse/app/components/signup-cta.js | 12 +- .../discourse/app/components/tag-info.js | 71 +++++----- .../app/components/topic-list-item.js | 2 + .../app/components/user-card-contents.js | 16 ++- .../discourse/app/controllers/auth-token.js | 11 +- .../app/controllers/avatar-selector.js | 17 ++- .../discourse/app/controllers/composer.js | 43 +++--- .../app/controllers/discovery/categories.js | 19 +-- .../app/controllers/discovery/topics.js | 21 +-- .../app/controllers/full-page-search.js | 31 +++-- .../discourse/app/controllers/history.js | 29 ++-- .../discourse/app/controllers/login.js | 111 +++++++++------- .../app/controllers/password-reset.js | 13 +- .../app/controllers/preferences/account.js | 28 ++-- .../controllers/preferences/second-factor.js | 40 +++--- .../app/controllers/preferences/security.js | 98 +++++++------- .../app/controllers/second-factor-add-totp.js | 9 +- .../app/controllers/second-factor-auth.js | 3 +- .../discourse/app/controllers/tag-show.js | 3 +- .../discourse/app/controllers/tags-index.js | 37 +++--- .../discourse/app/controllers/topic.js | 87 ++++++------ .../controllers/user-private-messages-tags.js | 34 ++--- .../app/controllers/user-topics-list.js | 4 +- .../discourse/app/controllers/user.js | 16 ++- .../templates/components/categories-only.hbs | 2 +- .../components/category-permission-row.hbs | 2 +- .../templates/components/choose-message.hbs | 2 +- .../components/edit-category-tab.hbs | 2 +- .../components/emoji-group-buttons.hbs | 18 +-- .../app/templates/components/emoji-picker.hbs | 6 +- .../templates/components/flag-action-type.hbs | 4 +- .../components/group-card-contents.hbs | 8 +- .../components/group-imap-email-settings.hbs | 2 +- .../group-manage-email-settings.hbs | 4 +- .../app/templates/components/group-member.hbs | 2 +- .../components/group-smtp-email-settings.hbs | 2 +- .../templates/components/login-buttons.hbs | 2 +- .../templates/components/reviewable-item.hbs | 2 +- .../components/reviewable-post-edits.hbs | 2 +- .../components/reviewable-queued-post.hbs | 2 +- .../components/search-result-entry.hbs | 2 +- .../components/second-factor-form.hbs | 2 +- .../components/security-key-form.hbs | 2 +- .../templates/components/selected-posts.hbs | 6 +- .../app/templates/components/signup-cta.hbs | 2 +- .../app/templates/components/tag-info.hbs | 6 +- .../components/user-card-contents.hbs | 6 +- .../discourse/app/templates/composer.hbs | 10 +- .../templates/composer/dominating-topic.hbs | 2 +- .../app/templates/composer/education.hbs | 2 +- .../app/templates/composer/get-a-room.hbs | 2 +- .../templates/composer/group-mentioned.hbs | 2 +- .../app/templates/composer/similar-topics.hbs | 2 +- .../app/templates/discovery/categories.hbs | 2 +- .../app/templates/discovery/topics.hbs | 2 +- .../app/templates/full-page-search.hbs | 2 +- .../mobile/components/categories-only.hbs | 2 +- .../mobile/components/mobile-nav.hbs | 2 +- .../mobile/components/navigation-bar.hbs | 2 +- .../app/templates/mobile/discovery/topics.hbs | 2 +- .../app/templates/mobile/modal/login.hbs | 4 +- .../app/templates/modal/auth-token.hbs | 2 +- .../app/templates/modal/avatar-selector.hbs | 2 +- .../discourse/app/templates/modal/history.hbs | 6 +- .../app/templates/modal/insert-hyperlink.hbs | 2 +- .../discourse/app/templates/modal/login.hbs | 4 +- .../modal/second-factor-add-totp.hbs | 2 +- .../app/templates/password-reset.hbs | 2 +- .../templates/preferences-second-factor.hbs | 2 +- .../app/templates/preferences/account.hbs | 2 +- .../app/templates/preferences/security.hbs | 6 +- .../app/templates/second-factor-auth.hbs | 2 +- .../discourse/app/templates/tag/show.hbs | 2 +- .../discourse/app/templates/tags/index.hbs | 4 +- .../discourse/app/templates/topic.hbs | 6 +- .../templates/user-private-messages-tags.hbs | 4 +- .../app/templates/user-topics-list.hbs | 2 +- .../discourse/app/templates/user.hbs | 2 +- .../plugin-outlet-connector-class-test.js | 4 +- .../tests/acceptance/user-menu-test.js | 19 --- .../fixtures/concerns/notification-types.js | 1 + .../multi-select/selected-category.hbs | 2 +- .../templates/components/selected-choice.hbs | 2 +- .../addon/components/styling-preview.js | 6 +- .../wizard/addon/components/wizard-step.js | 14 +- .../templates/components/styling-preview.hbs | 4 +- .../templates/components/wizard-step.hbs | 10 +- lib/tasks/javascript.rake | 2 +- .../discourse-local-dates-create-form.js | 8 +- .../discourse-local-dates-create-form.hbs | 2 +- .../controllers/poll-ui-builder.js | 6 + .../templates/modal/poll-ui-builder.hbs | 6 +- 135 files changed, 930 insertions(+), 731 deletions(-) diff --git a/.template-lintrc.js b/.template-lintrc.js index e3fc07b48d6..b5095385b21 100644 --- a/.template-lintrc.js +++ b/.template-lintrc.js @@ -3,6 +3,7 @@ module.exports = { extends: "discourse:recommended", rules: { + "no-action-modifiers": true, "no-capital-arguments": false, // TODO: we extensively use `args` argument name "no-curly-component-invocation": { allow: [ diff --git a/app/assets/javascripts/admin/addon/components/admin-editable-field.js b/app/assets/javascripts/admin/addon/components/admin-editable-field.js index 993a3ed6752..892a3208e86 100644 --- a/app/assets/javascripts/admin/addon/components/admin-editable-field.js +++ b/app/assets/javascripts/admin/addon/components/admin-editable-field.js @@ -1,4 +1,6 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; + export default Component.extend({ tagName: "", @@ -10,12 +12,14 @@ export default Component.extend({ this.set("editing", false); }, - actions: { - edit() { - this.set("buffer", this.value); - this.toggleProperty("editing"); - }, + @action + edit(event) { + event?.preventDefault(); + this.set("buffer", this.value); + this.toggleProperty("editing"); + }, + actions: { save() { // Action has to toggle 'editing' property. this.action(this.buffer); diff --git a/app/assets/javascripts/admin/addon/components/admin-theme-editor.js b/app/assets/javascripts/admin/addon/components/admin-theme-editor.js index 014055c1498..de8f0635791 100644 --- a/app/assets/javascripts/admin/addon/components/admin-theme-editor.js +++ b/app/assets/javascripts/admin/addon/components/admin-theme-editor.js @@ -3,6 +3,7 @@ import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; import { fmt } from "discourse/lib/computed"; import { isDocumentRTL } from "discourse/lib/text-direction"; +import { action } from "@ember/object"; import { next } from "@ember/runloop"; export default Component.extend({ @@ -91,15 +92,26 @@ export default Component.extend({ return this.theme.getError(target, fieldName); }, + @action + toggleShowAdvanced(event) { + event?.preventDefault(); + this.toggleProperty("showAdvanced"); + }, + + @action + toggleAddField(event) { + event?.preventDefault(); + this.toggleProperty("addingField"); + }, + + @action + toggleMaximize(event) { + event?.preventDefault(); + this.toggleProperty("maximized"); + next(() => this.appEvents.trigger("ace:resize")); + }, + actions: { - toggleShowAdvanced() { - this.toggleProperty("showAdvanced"); - }, - - toggleAddField() { - this.toggleProperty("addingField"); - }, - cancelAddField() { this.set("addingField", false); }, @@ -114,11 +126,6 @@ export default Component.extend({ this.fieldAdded(this.currentTargetName, name); }, - toggleMaximize() { - this.toggleProperty("maximized"); - next(() => this.appEvents.trigger("ace:resize")); - }, - onlyOverriddenChanged(value) { this.onlyOverriddenChanged(value); }, diff --git a/app/assets/javascripts/admin/addon/components/ip-lookup.js b/app/assets/javascripts/admin/addon/components/ip-lookup.js index 222e04b9ab6..02a52405645 100644 --- a/app/assets/javascripts/admin/addon/components/ip-lookup.js +++ b/app/assets/javascripts/admin/addon/components/ip-lookup.js @@ -1,6 +1,6 @@ import AdminUser from "admin/models/admin-user"; import Component from "@ember/component"; -import EmberObject from "@ember/object"; +import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; import { ajax } from "discourse/lib/ajax"; import copyText from "discourse/lib/copy-text"; @@ -21,6 +21,12 @@ export default Component.extend({ return Math.max(visible, total); }, + @action + hide(event) { + event?.preventDefault(); + this.set("show", false); + }, + actions: { lookup() { this.set("show", true); @@ -55,10 +61,6 @@ export default Component.extend({ } }, - hide() { - this.set("show", false); - }, - copy() { let text = `IP: ${this.ip}\n`; const location = this.location; diff --git a/app/assets/javascripts/admin/addon/components/themes-list-item.js b/app/assets/javascripts/admin/addon/components/themes-list-item.js index e2237e249ba..29158c44967 100644 --- a/app/assets/javascripts/admin/addon/components/themes-list-item.js +++ b/app/assets/javascripts/admin/addon/components/themes-list-item.js @@ -3,6 +3,7 @@ import discourseComputed from "discourse-common/utils/decorators"; import Component from "@ember/component"; import { escape } from "pretty-text/sanitizer"; import { iconHTML } from "discourse-common/lib/icon-library"; +import { action } from "@ember/object"; const MAX_COMPONENTS = 4; @@ -59,9 +60,9 @@ export default Component.extend({ return childrenCount - MAX_COMPONENTS; }, - actions: { - toggleChildrenExpanded() { - this.toggleProperty("childrenExpanded"); - }, + @action + toggleChildrenExpanded(event) { + event?.preventDefault(); + this.toggleProperty("childrenExpanded"); }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js b/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js index 2b380818269..5b098387ef8 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js @@ -133,6 +133,12 @@ export default class AdminBadgesShowController extends Controller.extend( this.buffered.set("image_url", null); } + @action + showPreview(badge, explain, event) { + event?.preventDefault(); + this.send("preview", badge, explain); + } + @action save() { if (!this.saving) { diff --git a/app/assets/javascripts/admin/addon/controllers/admin-email-bounced.js b/app/assets/javascripts/admin/addon/controllers/admin-email-bounced.js index 15005495645..c7a8ec98a60 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-email-bounced.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-email-bounced.js @@ -2,8 +2,15 @@ import AdminEmailLogsController from "admin/controllers/admin-email-logs"; import { INPUT_DELAY } from "discourse-common/config/environment"; import discourseDebounce from "discourse-common/lib/debounce"; import { observes } from "discourse-common/utils/decorators"; +import { action } from "@ember/object"; export default AdminEmailLogsController.extend({ + @action + handleShowIncomingEmail(id, event) { + event?.preventDefault(); + this.send("showIncomingEmail", id); + }, + @observes("filter.{status,user,address,type}") filterEmailLogs() { discourseDebounce(this, this.loadLogs, INPUT_DELAY); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-email-preview-digest.js b/app/assets/javascripts/admin/addon/controllers/admin-email-preview-digest.js index 50d74d35d8e..84f85eea6d3 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-email-preview-digest.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-email-preview-digest.js @@ -1,7 +1,7 @@ import { empty, notEmpty, or } from "@ember/object/computed"; import Controller from "@ember/controller"; import EmailPreview from "admin/models/email-preview"; -import { get } from "@ember/object"; +import { action, get } from "@ember/object"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { inject as service } from "@ember/service"; @@ -14,6 +14,12 @@ export default Controller.extend({ showSendEmailForm: notEmpty("model.html_content"), htmlEmpty: empty("model.html_content"), + @action + toggleShowHtml(event) { + event?.preventDefault(); + this.toggleProperty("showHtml"); + }, + actions: { updateUsername(selected) { this.set("username", get(selected, "firstObject")); @@ -39,10 +45,6 @@ export default Controller.extend({ }); }, - toggleShowHtml() { - this.toggleProperty("showHtml"); - }, - sendEmail() { this.set("sendingEmail", true); this.set("sentEmail", false); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-email-rejected.js b/app/assets/javascripts/admin/addon/controllers/admin-email-rejected.js index 89c67f3cf95..6e4ce786568 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-email-rejected.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-email-rejected.js @@ -3,6 +3,7 @@ import { INPUT_DELAY } from "discourse-common/config/environment"; import IncomingEmail from "admin/models/incoming-email"; import discourseDebounce from "discourse-common/lib/debounce"; import { observes } from "discourse-common/utils/decorators"; +import { action } from "@ember/object"; export default AdminEmailLogsController.extend({ @observes("filter.{status,from,to,subject,error}") @@ -10,6 +11,12 @@ export default AdminEmailLogsController.extend({ discourseDebounce(this, this.loadLogs, IncomingEmail, INPUT_DELAY); }, + @action + handleShowIncomingEmail(id, event) { + event?.preventDefault(); + this.send("showIncomingEmail", id); + }, + actions: { loadMore() { this.loadLogs(IncomingEmail, true); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-logs-screened-ip-addresses.js b/app/assets/javascripts/admin/addon/controllers/admin-logs-screened-ip-addresses.js index 0e8b43f9c13..bc11ec51f46 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-logs-screened-ip-addresses.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-logs-screened-ip-addresses.js @@ -6,6 +6,7 @@ import discourseDebounce from "discourse-common/lib/debounce"; import { exportEntity } from "discourse/lib/export-csv"; import { observes } from "discourse-common/utils/decorators"; import { outputExportResult } from "discourse/lib/export-result"; +import { action } from "@ember/object"; import { inject as service } from "@ember/service"; export default Controller.extend({ @@ -26,6 +27,15 @@ export default Controller.extend({ discourseDebounce(this, this._debouncedShow, INPUT_DELAY); }, + @action + edit(record, event) { + event?.preventDefault(); + if (!record.get("editing")) { + this.set("savedIpAddress", record.get("ip_address")); + } + record.set("editing", true); + }, + actions: { allow(record) { record.set("action_name", "do_nothing"); @@ -37,13 +47,6 @@ export default Controller.extend({ record.save(); }, - edit(record) { - if (!record.get("editing")) { - this.set("savedIpAddress", record.get("ip_address")); - } - record.set("editing", true); - }, - cancel(record) { const savedIpAddress = this.savedIpAddress; if (savedIpAddress && record.get("editing")) { diff --git a/app/assets/javascripts/admin/addon/controllers/admin-logs-staff-action-logs.js b/app/assets/javascripts/admin/addon/controllers/admin-logs-staff-action-logs.js index 670d48c17b9..a8a297d47c4 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-logs-staff-action-logs.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-logs-staff-action-logs.js @@ -1,5 +1,5 @@ import Controller from "@ember/controller"; -import EmberObject from "@ember/object"; +import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; import { exportEntity } from "discourse/lib/export-csv"; @@ -31,11 +31,13 @@ export default Controller.extend({ this.set( "userHistoryActions", result.extras.user_history_actions - .map((action) => ({ - id: action.id, - action_id: action.action_id, - name: I18n.t("admin.logs.staff_actions.actions." + action.id), - name_raw: action.id, + .map((historyAction) => ({ + id: historyAction.id, + action_id: historyAction.action_id, + name: I18n.t( + "admin.logs.staff_actions.actions." + historyAction.id + ), + name_raw: historyAction.id, })) .sort((a, b) => a.name.localeCompare(b.name)) ); @@ -75,61 +77,74 @@ export default Controller.extend({ this.scheduleRefresh(); }, - actions: { - filterActionIdChanged(filterActionId) { - if (filterActionId) { - this.changeFilters({ - action_name: filterActionId, - action_id: this.userHistoryActions.findBy("id", filterActionId) - .action_id, - }); - } - }, - - clearFilter(key) { - if (key === "actionFilter") { - this.set("filterActionId", null); - this.changeFilters({ - action_name: null, - action_id: null, - custom_type: null, - }); - } else { - this.changeFilters({ [key]: null }); - } - }, - - clearAllFilters() { - this.set("filterActionId", null); - this.resetFilters(); - }, - - filterByAction(logItem) { + @action + filterActionIdChanged(filterActionId) { + if (filterActionId) { this.changeFilters({ - action_name: logItem.get("action_name"), - action_id: logItem.get("action"), - custom_type: logItem.get("custom_type"), + action_name: filterActionId, + action_id: this.userHistoryActions.findBy("id", filterActionId) + .action_id, }); - }, + } + }, - filterByStaffUser(acting_user) { - this.changeFilters({ acting_user: acting_user.username }); - }, + @action + clearFilter(key, event) { + event?.preventDefault(); + if (key === "actionFilter") { + this.set("filterActionId", null); + this.changeFilters({ + action_name: null, + action_id: null, + custom_type: null, + }); + } else { + this.changeFilters({ [key]: null }); + } + }, - filterByTargetUser(target_user) { - this.changeFilters({ target_user: target_user.username }); - }, + @action + clearAllFilters(event) { + event?.preventDefault(); + this.set("filterActionId", null); + this.resetFilters(); + }, - filterBySubject(subject) { - this.changeFilters({ subject }); - }, + @action + filterByAction(logItem, event) { + event?.preventDefault(); + this.changeFilters({ + action_name: logItem.get("action_name"), + action_id: logItem.get("action"), + custom_type: logItem.get("custom_type"), + }); + }, - exportStaffActionLogs() { - exportEntity("staff_action").then(outputExportResult); - }, + @action + filterByStaffUser(acting_user, event) { + event?.preventDefault(); + this.changeFilters({ acting_user: acting_user.username }); + }, - loadMore() { - this.model.loadMore(); - }, + @action + filterByTargetUser(target_user, event) { + event?.preventDefault(); + this.changeFilters({ target_user: target_user.username }); + }, + + @action + filterBySubject(subject, event) { + event?.preventDefault(); + this.changeFilters({ subject }); + }, + + @action + exportStaffActionLogs() { + exportEntity("staff_action").then(outputExportResult); + }, + + @action + loadMore() { + this.model.loadMore(); }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show-events.js b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show-events.js index 71990f47a34..84b7650ff16 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show-events.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show-events.js @@ -1,5 +1,6 @@ import Controller from "@ember/controller"; import { ajax } from "discourse/lib/ajax"; +import { action } from "@ember/object"; import { alias } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import { popupAjaxError } from "discourse/lib/ajax-error"; @@ -43,6 +44,23 @@ export default Controller.extend({ } }, + @action + showInserted(event) { + event?.preventDefault(); + const webHookId = this.get("model.extras.web_hook_id"); + + ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, { + type: "GET", + data: { ids: this.incomingEventIds }, + }).then((data) => { + const objects = data.map((webHookEvent) => + this.store.createRecord("web-hook-event", webHookEvent) + ); + this.model.unshiftObjects(objects); + this.set("incomingEventIds", []); + }); + }, + actions: { loadMore() { this.model.loadMore(); @@ -61,20 +79,5 @@ export default Controller.extend({ popupAjaxError(error); }); }, - - showInserted() { - const webHookId = this.get("model.extras.web_hook_id"); - - ajax(`/admin/api/web_hooks/${webHookId}/events/bulk`, { - type: "GET", - data: { ids: this.incomingEventIds }, - }).then((data) => { - const objects = data.map((event) => - this.store.createRecord("web-hook-event", event) - ); - this.model.unshiftObjects(objects); - this.set("incomingEventIds", []); - }); - }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/modals/admin-uploaded-image-list.js b/app/assets/javascripts/admin/addon/controllers/modals/admin-uploaded-image-list.js index 7ab4cb69848..8a60037a5ba 100644 --- a/app/assets/javascripts/admin/addon/controllers/modals/admin-uploaded-image-list.js +++ b/app/assets/javascripts/admin/addon/controllers/modals/admin-uploaded-image-list.js @@ -1,5 +1,6 @@ import { observes, on } from "discourse-common/utils/decorators"; import Controller from "@ember/controller"; +import { action } from "@ember/object"; import ModalFunctionality from "discourse/mixins/modal-functionality"; export default Controller.extend(ModalFunctionality, { @@ -10,15 +11,17 @@ export default Controller.extend(ModalFunctionality, { this.set("images", value && value.length ? value.split("|") : []); }, + @action + remove(url, event) { + event?.preventDefault(); + this.images.removeObject(url); + }, + actions: { uploadDone({ url }) { this.images.addObject(url); }, - remove(url) { - this.images.removeObject(url); - }, - close() { this.save(this.images.join("|")); this.send("closeModal"); diff --git a/app/assets/javascripts/admin/addon/templates/admin-badges/show.hbs b/app/assets/javascripts/admin/addon/templates/admin-badges/show.hbs index 31b16af4cc0..82492beecb4 100644 --- a/app/assets/javascripts/admin/addon/templates/admin-badges/show.hbs +++ b/app/assets/javascripts/admin/addon/templates/admin-badges/show.hbs @@ -88,9 +88,9 @@ {{#if this.hasQuery}} - {{i18n "admin.badges.preview.link_text"}} + {{i18n "admin.badges.preview.link_text"}} | - {{i18n "admin.badges.preview.plan_text"}} + {{i18n "admin.badges.preview.plan_text"}} {{#if this.preview_loading}} {{i18n "loading"}} {{/if}} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs b/app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs index d62cfdbd783..998174f20a9 100644 --- a/app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs +++ b/app/assets/javascripts/admin/addon/templates/components/admin-editable-field.hbs @@ -3,7 +3,7 @@ {{#if this.editing}} {{else}} - + {{this.value}} {{/if}} @@ -11,7 +11,7 @@
{{#if this.editing}} - {{i18n "cancel"}} + {{i18n "cancel"}} {{else}} {{/if}} diff --git a/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs b/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs index ec8ed70f301..a8c33e1da26 100644 --- a/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs +++ b/app/assets/javascripts/admin/addon/templates/components/admin-theme-editor.hbs @@ -13,7 +13,7 @@ {{#if this.allowAdvanced}}
  • - @@ -52,7 +52,7 @@ {{else}} - + {{d-icon "plus"}} {{/if}} @@ -61,7 +61,7 @@
  • - + {{d-icon this.maximizeIcon}}
  • diff --git a/app/assets/javascripts/admin/addon/templates/components/ip-lookup.hbs b/app/assets/javascripts/admin/addon/templates/components/ip-lookup.hbs index f63ab16730a..31ec7f35a76 100644 --- a/app/assets/javascripts/admin/addon/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/admin/addon/templates/components/ip-lookup.hbs @@ -3,7 +3,7 @@ {{/if}} {{#if this.show}}
    - {{d-icon "times"}} + {{d-icon "times"}} {{#if this.copied}} {{else}} diff --git a/app/assets/javascripts/admin/addon/templates/components/themes-list-item.hbs b/app/assets/javascripts/admin/addon/templates/components/themes-list-item.hbs index 1710a14454f..21678c1a436 100644 --- a/app/assets/javascripts/admin/addon/templates/components/themes-list-item.hbs +++ b/app/assets/javascripts/admin/addon/templates/components/themes-list-item.hbs @@ -31,7 +31,7 @@ {{html-safe this.childrenString}} {{#if this.displayHasMore}} - + {{#if this.childrenExpanded}} {{i18n "admin.customize.theme.collapse"}} {{else}} diff --git a/app/assets/javascripts/admin/addon/templates/email-bounced.hbs b/app/assets/javascripts/admin/addon/templates/email-bounced.hbs index cf6ceb81bc4..6a47751d00d 100644 --- a/app/assets/javascripts/admin/addon/templates/email-bounced.hbs +++ b/app/assets/javascripts/admin/addon/templates/email-bounced.hbs @@ -30,7 +30,7 @@ {{l.to_address}} {{#if l.has_bounce_key}} - + {{l.email_type}} {{else}} @@ -39,7 +39,7 @@ {{#if l.has_bounce_key}} - + {{d-icon "info-circle"}} {{/if}} diff --git a/app/assets/javascripts/admin/addon/templates/email-preview-digest.hbs b/app/assets/javascripts/admin/addon/templates/email-preview-digest.hbs index d2ee4cad4d9..b00332840d5 100644 --- a/app/assets/javascripts/admin/addon/templates/email-preview-digest.hbs +++ b/app/assets/javascripts/admin/addon/templates/email-preview-digest.hbs @@ -15,11 +15,11 @@ {{#if this.showHtml}} {{i18n "admin.email.html"}} | - + {{i18n "admin.email.text"}} {{else}} - {{i18n "admin.email.html"}} | + {{i18n "admin.email.html"}} | {{i18n "admin.email.text"}} {{/if}}
    diff --git a/app/assets/javascripts/admin/addon/templates/email-rejected.hbs b/app/assets/javascripts/admin/addon/templates/email-rejected.hbs index 21dcbba21da..19033f5f6f9 100644 --- a/app/assets/javascripts/admin/addon/templates/email-rejected.hbs +++ b/app/assets/javascripts/admin/addon/templates/email-rejected.hbs @@ -48,10 +48,10 @@ {{email.subject}} - {{email.error}} + {{email.error}} - + {{d-icon "info-circle"}} diff --git a/app/assets/javascripts/admin/addon/templates/logs/screened-ip-addresses.hbs b/app/assets/javascripts/admin/addon/templates/logs/screened-ip-addresses.hbs index 63489dcdee9..ab20b2316b6 100644 --- a/app/assets/javascripts/admin/addon/templates/logs/screened-ip-addresses.hbs +++ b/app/assets/javascripts/admin/addon/templates/logs/screened-ip-addresses.hbs @@ -27,7 +27,7 @@ {{#if item.editing}} {{else}} - + {{#if item.isRange}} {{item.ip_address}} {{else}} diff --git a/app/assets/javascripts/admin/addon/templates/logs/staff-action-logs.hbs b/app/assets/javascripts/admin/addon/templates/logs/staff-action-logs.hbs index bcee25b4396..d2e46867c94 100644 --- a/app/assets/javascripts/admin/addon/templates/logs/staff-action-logs.hbs +++ b/app/assets/javascripts/admin/addon/templates/logs/staff-action-logs.hbs @@ -1,29 +1,29 @@
    {{#if this.filtersExists}} - {{item.actionName}} + {{item.actionName}}
    {{#if item.target_user}} {{avatar item.target_user imageSize="tiny"}} - {{item.target_user.username}} + {{item.target_user.username}} {{/if}} {{#if item.subject}} - {{item.subject}} + {{item.subject}} {{/if}}
    @@ -89,10 +89,10 @@ diff --git a/app/assets/javascripts/admin/addon/templates/modal/admin-uploaded-image-list.hbs b/app/assets/javascripts/admin/addon/templates/modal/admin-uploaded-image-list.hbs index a7aefa73714..aca6fef6f47 100644 --- a/app/assets/javascripts/admin/addon/templates/modal/admin-uploaded-image-list.hbs +++ b/app/assets/javascripts/admin/addon/templates/modal/admin-uploaded-image-list.hbs @@ -1,7 +1,7 @@
    {{#each this.images as |image|}} - + {{bound-avatar-template image "huge"}} {{else}} diff --git a/app/assets/javascripts/admin/addon/templates/web-hooks-show-events.hbs b/app/assets/javascripts/admin/addon/templates/web-hooks-show-events.hbs index 2b5c768def3..c9791f0fc4f 100644 --- a/app/assets/javascripts/admin/addon/templates/web-hooks-show-events.hbs +++ b/app/assets/javascripts/admin/addon/templates/web-hooks-show-events.hbs @@ -21,7 +21,7 @@
    {{#if this.hasIncoming}} - + {{/if}} diff --git a/app/assets/javascripts/discourse/app/components/categories-only.js b/app/assets/javascripts/discourse/app/components/categories-only.js index 002ddf82e3e..d480f464555 100644 --- a/app/assets/javascripts/discourse/app/components/categories-only.js +++ b/app/assets/javascripts/discourse/app/components/categories-only.js @@ -50,7 +50,8 @@ export default Component.extend({ }, @action - toggleShowMuted() { + toggleShowMuted(event) { + event?.preventDefault(); this.toggleProperty("showMuted"); }, }); 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 2e41aa6e63c..46eb3690f97 100644 --- a/app/assets/javascripts/discourse/app/components/category-permission-row.js +++ b/app/assets/javascripts/discourse/app/components/category-permission-row.js @@ -1,3 +1,4 @@ +import { action } from "@ember/object"; import { alias, equal } from "@ember/object/computed"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; import Component from "@ember/component"; @@ -92,11 +93,13 @@ export default Component.extend({ this.category.updatePermission(this.group_name, type); }, - actions: { - removeRow() { - this.category.removePermission(this.group_name); - }, + @action + removeRow(event) { + event?.preventDefault(); + this.category.removePermission(this.group_name); + }, + actions: { setPermissionReply() { if (this.type <= PermissionType.CREATE_POST) { this.updatePermission(PermissionType.READONLY); diff --git a/app/assets/javascripts/discourse/app/components/choose-message.js b/app/assets/javascripts/discourse/app/components/choose-message.js index 09d9a9123cb..15f0424f60e 100644 --- a/app/assets/javascripts/discourse/app/components/choose-message.js +++ b/app/assets/javascripts/discourse/app/components/choose-message.js @@ -1,6 +1,6 @@ import Component from "@ember/component"; import discourseDebounce from "discourse-common/lib/debounce"; -import { get } from "@ember/object"; +import { action, get } from "@ember/object"; import { isEmpty } from "@ember/utils"; import { next } from "@ember/runloop"; import { observes } from "discourse-common/utils/decorators"; @@ -63,12 +63,11 @@ export default Component.extend({ ); }, - actions: { - chooseMessage(message) { - const messageId = get(message, "id"); - this.set("selectedTopicId", messageId); - next(() => $(`#choose-message-${messageId}`).prop("checked", "true")); - return false; - }, + @action + chooseMessage(message, event) { + event?.preventDefault(); + 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/composer-messages.js b/app/assets/javascripts/discourse/app/components/composer-messages.js index 40832b5df17..275d1c534c5 100644 --- a/app/assets/javascripts/discourse/app/components/composer-messages.js +++ b/app/assets/javascripts/discourse/app/components/composer-messages.js @@ -1,5 +1,5 @@ import Component from "@ember/component"; -import EmberObject from "@ember/object"; +import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; import LinkLookup from "discourse/lib/link-lookup"; import { not } from "@ember/object/computed"; @@ -54,11 +54,13 @@ export default Component.extend({ this.set("messageCount", messages.get("length")); }, - actions: { - closeMessage(message) { - this._removeMessage(message); - }, + @action + closeMessage(message, event) { + event?.preventDefault(); + this._removeMessage(message); + }, + actions: { hideMessage(message) { this._removeMessage(message); // kind of hacky but the visibility depends on this diff --git a/app/assets/javascripts/discourse/app/components/edit-category-tab.js b/app/assets/javascripts/discourse/app/components/edit-category-tab.js index e96720db36d..5619e8c7531 100644 --- a/app/assets/javascripts/discourse/app/components/edit-category-tab.js +++ b/app/assets/javascripts/discourse/app/components/edit-category-tab.js @@ -2,6 +2,7 @@ import Component from "@ember/component"; import DiscourseURL from "discourse/lib/url"; import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; +import { action } from "@ember/object"; import { empty } from "@ember/object/computed"; import getURL from "discourse-common/lib/get-url"; import { propertyEqual } from "discourse/lib/computed"; @@ -49,13 +50,12 @@ export default Component.extend({ return getURL(`/c/${slugPart}/edit/${this.tab}`); }, - actions: { - select() { - this.set("selectedTab", this.tab); - - if (!this.newCategory) { - DiscourseURL.routeTo(this.fullSlug); - } - }, + @action + select(event) { + event?.preventDefault(); + this.set("selectedTab", this.tab); + if (!this.newCategory) { + DiscourseURL.routeTo(this.fullSlug); + } }, }); diff --git a/app/assets/javascripts/discourse/app/components/emoji-picker.js b/app/assets/javascripts/discourse/app/components/emoji-picker.js index 3e0646f8939..8a5dec83a65 100644 --- a/app/assets/javascripts/discourse/app/components/emoji-picker.js +++ b/app/assets/javascripts/discourse/app/components/emoji-picker.js @@ -240,7 +240,8 @@ export default Component.extend({ }, @action - onCategorySelection(sectionName) { + onCategorySelection(sectionName, event) { + event?.preventDefault(); const section = document.querySelector( `.emoji-picker-emoji-area .section[data-section="${sectionName}"]` ); diff --git a/app/assets/javascripts/discourse/app/components/group-card-contents.js b/app/assets/javascripts/discourse/app/components/group-card-contents.js index cb073a6733d..8c7e5d93c55 100644 --- a/app/assets/javascripts/discourse/app/components/group-card-contents.js +++ b/app/assets/javascripts/discourse/app/components/group-card-contents.js @@ -1,3 +1,4 @@ +import { action } from "@ember/object"; import { alias, gt } from "@ember/object/computed"; import CardContentsBase from "discourse/mixins/card-contents-base"; import CleansUp from "discourse/mixins/cleans-up"; @@ -70,11 +71,22 @@ export default Component.extend(CardContentsBase, CleansUp, { this._close(); }, - actions: { - close() { - this._close(); - }, + @action + close(event) { + event?.preventDefault(); + this._close(); + }, + @action + handleShowGroup(group, event) { + event?.preventDefault(); + // Invokes `showGroup` argument. Convert to `this.args.showGroup` when + // refactoring this to a glimmer component. + this.showGroup(group); + this._close(); + }, + + actions: { cancelFilter() { const postStream = this.postStream; postStream.cancelFilter(); @@ -90,8 +102,7 @@ export default Component.extend(CardContentsBase, CleansUp, { }, showGroup(group) { - this.showGroup(group); - this._close(); + this.handleShowGroup(group); }, }, }); diff --git a/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js b/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js index 190a9fb2f97..a437f42d445 100644 --- a/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js +++ b/app/assets/javascripts/discourse/app/components/group-imap-email-settings.js @@ -53,7 +53,8 @@ export default Component.extend({ }, @action - prefillSettings(provider) { + prefillSettings(provider, event) { + event?.preventDefault(); this.form.setProperties(emailProviderDefaultSettings(provider, "imap")); }, diff --git a/app/assets/javascripts/discourse/app/components/group-member.js b/app/assets/javascripts/discourse/app/components/group-member.js index 64b2570dee2..08d1097b426 100644 --- a/app/assets/javascripts/discourse/app/components/group-member.js +++ b/app/assets/javascripts/discourse/app/components/group-member.js @@ -1,10 +1,12 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; + export default Component.extend({ classNames: ["item"], - actions: { - remove() { - this.removeAction(this.member); - }, + @action + remove(event) { + event?.preventDefault(); + this.removeAction(this.member); }, }); diff --git a/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js b/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js index 5456c75a792..d4b4bcb9de4 100644 --- a/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js +++ b/app/assets/javascripts/discourse/app/components/group-smtp-email-settings.js @@ -43,7 +43,8 @@ export default Component.extend({ }, @action - prefillSettings(provider) { + prefillSettings(provider, event) { + event?.preventDefault(); this.form.setProperties(emailProviderDefaultSettings(provider, "smtp")); }, diff --git a/app/assets/javascripts/discourse/app/components/mobile-nav.js b/app/assets/javascripts/discourse/app/components/mobile-nav.js index bdbb2cc85a2..f0ea3e6138d 100644 --- a/app/assets/javascripts/discourse/app/components/mobile-nav.js +++ b/app/assets/javascripts/discourse/app/components/mobile-nav.js @@ -1,5 +1,6 @@ import { on } from "discourse-common/utils/decorators"; import Component from "@ember/component"; +import { action } from "@ember/object"; import { next } from "@ember/runloop"; import { inject as service } from "@ember/service"; import deprecated from "discourse-common/lib/deprecated"; @@ -56,27 +57,27 @@ export default Component.extend({ this.router.off("routeDidChange", this, this.currentRouteChanged); }, - actions: { - toggleExpanded() { - this.toggleProperty("expanded"); + @action + toggleExpanded(event) { + event?.preventDefault(); + this.toggleProperty("expanded"); - next(() => { - if (this.expanded) { - $(window) - .off("click.mobile-nav") - .on("click.mobile-nav", (e) => { - if (!this.element || this.isDestroying || this.isDestroyed) { - return; - } + next(() => { + if (this.expanded) { + $(window) + .off("click.mobile-nav") + .on("click.mobile-nav", (e) => { + if (!this.element || this.isDestroying || this.isDestroyed) { + return; + } - const expander = this.element.querySelector(".expander"); - if (expander && e.target !== expander) { - this.set("expanded", false); - $(window).off("click.mobile-nav"); - } - }); - } - }); - }, + const expander = this.element.querySelector(".expander"); + if (expander && e.target !== expander) { + this.set("expanded", false); + $(window).off("click.mobile-nav"); + } + }); + } + }); }, }); diff --git a/app/assets/javascripts/discourse/app/components/navigation-bar.js b/app/assets/javascripts/discourse/app/components/navigation-bar.js index ab9a5b9c714..5ede7b2bf4c 100644 --- a/app/assets/javascripts/discourse/app/components/navigation-bar.js +++ b/app/assets/javascripts/discourse/app/components/navigation-bar.js @@ -1,5 +1,6 @@ import discourseComputed, { observes } from "discourse-common/utils/decorators"; import Component from "@ember/component"; +import { action } from "@ember/object"; import DiscourseURL from "discourse/lib/url"; import FilterModeMixin from "discourse/mixins/filter-mode"; import { next } from "@ember/runloop"; @@ -61,33 +62,33 @@ export default Component.extend(FilterModeMixin, { DiscourseURL.appEvents.off("dom:clean", this, this.ensureDropClosed); }, - actions: { - toggleDrop() { - this.set("expanded", !this.expanded); + @action + toggleDrop(event) { + event?.preventDefault(); + this.set("expanded", !this.expanded); - if (this.expanded) { - DiscourseURL.appEvents.on("dom:clean", this, this.ensureDropClosed); + if (this.expanded) { + DiscourseURL.appEvents.on("dom:clean", this, this.ensureDropClosed); - next(() => { - if (!this.expanded) { - return; - } + next(() => { + if (!this.expanded) { + return; + } - $(this.element.querySelector(".drop a")).on("click", () => { - this.element.querySelector(".drop").style.display = "none"; + $(this.element.querySelector(".drop a")).on("click", () => { + this.element.querySelector(".drop").style.display = "none"; - next(() => { - this.ensureDropClosed(); - }); - return true; - }); - - $(window).on("click.navigation-bar", () => { + next(() => { this.ensureDropClosed(); - return true; }); + return true; }); - } - }, + + $(window).on("click.navigation-bar", () => { + this.ensureDropClosed(); + return true; + }); + }); + } }, }); diff --git a/app/assets/javascripts/discourse/app/components/reviewable-item.js b/app/assets/javascripts/discourse/app/components/reviewable-item.js index ff67e672a81..73c90d665a9 100644 --- a/app/assets/javascripts/discourse/app/components/reviewable-item.js +++ b/app/assets/javascripts/discourse/app/components/reviewable-item.js @@ -7,7 +7,7 @@ import { classify, dasherize } from "@ember/string"; import discourseComputed, { bind } from "discourse-common/utils/decorators"; import optionalService from "discourse/lib/optional-service"; import { popupAjaxError } from "discourse/lib/ajax-error"; -import { set } from "@ember/object"; +import { action, set } from "@ember/object"; import showModal from "discourse/lib/show-modal"; let _components = {}; @@ -121,7 +121,7 @@ export default Component.extend({ }, @bind - _performConfirmed(action) { + _performConfirmed(performableAction) { let reviewable = this.reviewable; let performAction = () => { @@ -140,7 +140,7 @@ export default Component.extend({ }); return ajax( - `/review/${reviewable.id}/perform/${action.id}?version=${version}`, + `/review/${reviewable.id}/perform/${performableAction.id}?version=${version}`, { type: "PUT", data, @@ -173,13 +173,16 @@ export default Component.extend({ .finally(() => this.set("updating", false)); }; - if (action.client_action) { - let actionMethod = this[`client${classify(action.client_action)}`]; + if (performableAction.client_action) { + let actionMethod = + this[`client${classify(performableAction.client_action)}`]; if (actionMethod) { return actionMethod.call(this, reviewable, performAction); } else { // eslint-disable-next-line no-console - console.error(`No handler for ${action.client_action} found`); + console.error( + `No handler for ${performableAction.client_action} found` + ); return; } } else { @@ -209,14 +212,16 @@ export default Component.extend({ } }, - actions: { - explainReviewable(reviewable) { - showModal("explain-reviewable", { - title: "review.explain.title", - model: reviewable, - }); - }, + @action + explainReviewable(reviewable, event) { + event?.preventDefault(); + showModal("explain-reviewable", { + title: "review.explain.title", + model: reviewable, + }); + }, + actions: { edit() { this.set("editing", true); this.set("_updates", { payload: {} }); @@ -259,18 +264,18 @@ export default Component.extend({ set(this._updates, fieldId, event.target.value); }, - perform(action) { + perform(performableAction) { if (this.updating) { return; } - let msg = action.get("confirm_message"); - let requireRejectReason = action.get("require_reject_reason"); - let customModal = action.get("custom_modal"); + let msg = performableAction.get("confirm_message"); + let requireRejectReason = performableAction.get("require_reject_reason"); + let customModal = performableAction.get("custom_modal"); if (msg) { bootbox.confirm(msg, (answer) => { if (answer) { - return this._performConfirmed(action); + return this._performConfirmed(performableAction); } }); } else if (requireRejectReason) { @@ -279,7 +284,7 @@ export default Component.extend({ model: this.reviewable, }).setProperties({ performConfirmed: this._performConfirmed, - action, + action: performableAction, }); } else if (customModal) { showModal(customModal, { @@ -287,10 +292,10 @@ export default Component.extend({ model: this.reviewable, }).setProperties({ performConfirmed: this._performConfirmed, - action, + action: performableAction, }); } else { - return this._performConfirmed(action); + return this._performConfirmed(performableAction); } }, }, diff --git a/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js b/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js index 84c27f152f5..75b8692a49f 100644 --- a/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js +++ b/app/assets/javascripts/discourse/app/components/reviewable-post-edits.js @@ -1,5 +1,6 @@ import Component from "@ember/component"; import discourseComputed from "discourse-common/utils/decorators"; +import { action } from "@ember/object"; import { gt } from "@ember/object/computed"; import { historyHeat } from "discourse/widgets/post-edits-indicator"; import { longDate } from "discourse/lib/formatter"; @@ -18,18 +19,18 @@ export default Component.extend({ return longDate(updatedAt); }, - actions: { - showEditHistory() { - let postId = this.get("reviewable.post_id"); - this.store.find("post", postId).then((post) => { - let historyController = showModal("history", { - model: post, - modalClass: "history-modal", - }); - historyController.refresh(postId, "latest"); - historyController.set("post", post); - historyController.set("topicController", null); + @action + showEditHistory(event) { + event?.preventDefault(); + let postId = this.get("reviewable.post_id"); + this.store.find("post", postId).then((post) => { + let historyController = showModal("history", { + model: post, + modalClass: "history-modal", }); - }, + historyController.refresh(postId, "latest"); + historyController.set("post", post); + historyController.set("topicController", null); + }); }, }); diff --git a/app/assets/javascripts/discourse/app/components/reviewable-queued-post.js b/app/assets/javascripts/discourse/app/components/reviewable-queued-post.js index e40ab39ef7d..d06ddf3c1c5 100644 --- a/app/assets/javascripts/discourse/app/components/reviewable-queued-post.js +++ b/app/assets/javascripts/discourse/app/components/reviewable-queued-post.js @@ -1,10 +1,11 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; import showModal from "discourse/lib/show-modal"; export default Component.extend({ - actions: { - showRawEmail() { - showModal("raw-email").set("rawEmail", this.reviewable.payload.raw_email); - }, + @action + showRawEmail(event) { + event?.preventDefault(); + showModal("raw-email").set("rawEmail", this.reviewable.payload.raw_email); }, }); diff --git a/app/assets/javascripts/discourse/app/components/search-result-entry.js b/app/assets/javascripts/discourse/app/components/search-result-entry.js index 9312174ed18..deeaee78250 100644 --- a/app/assets/javascripts/discourse/app/components/search-result-entry.js +++ b/app/assets/javascripts/discourse/app/components/search-result-entry.js @@ -11,6 +11,7 @@ export default Component.extend({ @action logClick(topicId) { + // Important: Don't prevent default handling of clicks if (this.searchLogId && topicId) { logSearchLinkClick({ searchLogId: this.searchLogId, diff --git a/app/assets/javascripts/discourse/app/components/second-factor-form.js b/app/assets/javascripts/discourse/app/components/second-factor-form.js index 3f0c40b381e..e208a5f3145 100644 --- a/app/assets/javascripts/discourse/app/components/second-factor-form.js +++ b/app/assets/javascripts/discourse/app/components/second-factor-form.js @@ -1,4 +1,5 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; import I18n from "I18n"; import { SECOND_FACTOR_METHODS } from "discourse/models/user"; import discourseComputed from "discourse-common/utils/decorators"; @@ -48,15 +49,15 @@ export default Component.extend({ ); }, - actions: { - toggleSecondFactorMethod() { - const secondFactorMethod = this.secondFactorMethod; - this.set("secondFactorToken", ""); - if (secondFactorMethod === SECOND_FACTOR_METHODS.TOTP) { - this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE); - } else { - this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP); - } - }, + @action + toggleSecondFactorMethod(event) { + event?.preventDefault(); + const secondFactorMethod = this.secondFactorMethod; + this.set("secondFactorToken", ""); + if (secondFactorMethod === SECOND_FACTOR_METHODS.TOTP) { + this.set("secondFactorMethod", SECOND_FACTOR_METHODS.BACKUP_CODE); + } else { + this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP); + } }, }); diff --git a/app/assets/javascripts/discourse/app/components/security-key-form.js b/app/assets/javascripts/discourse/app/components/security-key-form.js index bd6d03f9f4e..dcc0725113f 100644 --- a/app/assets/javascripts/discourse/app/components/security-key-form.js +++ b/app/assets/javascripts/discourse/app/components/security-key-form.js @@ -1,12 +1,13 @@ import Component from "@ember/component"; +import { action } from "@ember/object"; import { SECOND_FACTOR_METHODS } from "discourse/models/user"; export default Component.extend({ - actions: { - useAnotherMethod() { - this.set("showSecurityKey", false); - this.set("showSecondFactor", true); - this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP); - }, + @action + useAnotherMethod(event) { + event?.preventDefault(); + this.set("showSecurityKey", false); + this.set("showSecondFactor", true); + this.set("secondFactorMethod", SECOND_FACTOR_METHODS.TOTP); }, }); diff --git a/app/assets/javascripts/discourse/app/components/signup-cta.js b/app/assets/javascripts/discourse/app/components/signup-cta.js index 5ea99dffe9b..a4d80fd611d 100644 --- a/app/assets/javascripts/discourse/app/components/signup-cta.js +++ b/app/assets/javascripts/discourse/app/components/signup-cta.js @@ -1,15 +1,19 @@ import Component from "@ember/component"; import discourseLater from "discourse-common/lib/later"; +import { action } from "@ember/object"; import { on } from "@ember/object/evented"; export default Component.extend({ action: "showCreateAccount", + @action + neverShow(event) { + event?.preventDefault(); + this.keyValueStore.setItem("anon-cta-never", "t"); + this.session.set("showSignupCta", false); + }, + actions: { - neverShow() { - this.keyValueStore.setItem("anon-cta-never", "t"); - this.session.set("showSignupCta", false); - }, hideForSession() { this.session.set("hideSignupCta", true); this.keyValueStore.setItem("anon-cta-hidden", Date.now()); diff --git a/app/assets/javascripts/discourse/app/components/tag-info.js b/app/assets/javascripts/discourse/app/components/tag-info.js index 6cb2a35dba8..21da9a92f1b 100644 --- a/app/assets/javascripts/discourse/app/components/tag-info.js +++ b/app/assets/javascripts/discourse/app/components/tag-info.js @@ -7,6 +7,7 @@ import discourseComputed from "discourse-common/utils/decorators"; import { isEmpty } from "@ember/utils"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { inject as service } from "@ember/service"; +import { action } from "@ember/object"; export default Component.extend({ dialog: service(), @@ -76,19 +77,49 @@ export default Component.extend({ .catch(popupAjaxError); }, + @action + edit(event) { + event?.preventDefault(); + this.setProperties({ + editing: true, + newTagName: this.tag.id, + newTagDescription: this.tagInfo.description, + }); + }, + + @action + unlinkSynonym(tag, event) { + event?.preventDefault(); + ajax(`/tag/${this.tagInfo.name}/synonyms/${tag.id}`, { + type: "DELETE", + }) + .then(() => this.tagInfo.synonyms.removeObject(tag)) + .catch(popupAjaxError); + }, + + @action + deleteSynonym(tag, event) { + event?.preventDefault(); + bootbox.confirm( + I18n.t("tagging.delete_synonym_confirm", { tag_name: tag.text }), + (result) => { + if (!result) { + return; + } + + tag + .destroyRecord() + .then(() => this.tagInfo.synonyms.removeObject(tag)) + .catch(popupAjaxError); + } + ); + }, + actions: { toggleEditControls() { this.toggleProperty("showEditControls"); }, - edit() { - this.setProperties({ - editing: true, - newTagName: this.tag.id, - newTagDescription: this.tagInfo.description, - }); - }, - cancelEditing() { this.set("editing", false); }, @@ -114,30 +145,6 @@ export default Component.extend({ this.deleteAction(this.tagInfo); }, - unlinkSynonym(tag) { - ajax(`/tag/${this.tagInfo.name}/synonyms/${tag.id}`, { - type: "DELETE", - }) - .then(() => this.tagInfo.synonyms.removeObject(tag)) - .catch(popupAjaxError); - }, - - deleteSynonym(tag) { - bootbox.confirm( - I18n.t("tagging.delete_synonym_confirm", { tag_name: tag.text }), - (result) => { - if (!result) { - return; - } - - tag - .destroyRecord() - .then(() => this.tagInfo.synonyms.removeObject(tag)) - .catch(popupAjaxError); - } - ); - }, - addSynonyms() { bootbox.confirm( I18n.t("tagging.add_synonyms_explanation", { diff --git a/app/assets/javascripts/discourse/app/components/topic-list-item.js b/app/assets/javascripts/discourse/app/components/topic-list-item.js index 972e8241732..2d6ce3e18fd 100644 --- a/app/assets/javascripts/discourse/app/components/topic-list-item.js +++ b/app/assets/javascripts/discourse/app/components/topic-list-item.js @@ -251,6 +251,7 @@ export default Component.extend({ if (wantsNewWindow(e)) { return true; } + e.preventDefault(); return this.navigateToTopic(topic, e.target.getAttribute("href")); } @@ -264,6 +265,7 @@ export default Component.extend({ if (wantsNewWindow(e)) { return true; } + e.preventDefault(); return this.navigateToTopic(topic, topic.lastUnreadUrl); } diff --git a/app/assets/javascripts/discourse/app/components/user-card-contents.js b/app/assets/javascripts/discourse/app/components/user-card-contents.js index 930f77a8c14..f114a1abf59 100644 --- a/app/assets/javascripts/discourse/app/components/user-card-contents.js +++ b/app/assets/javascripts/discourse/app/components/user-card-contents.js @@ -1,4 +1,4 @@ -import EmberObject, { set } from "@ember/object"; +import EmberObject, { action, set } from "@ember/object"; import { alias, and, gt, gte, not, or } from "@ember/object/computed"; import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { propertyNotEqual, setting } from "discourse/lib/computed"; @@ -220,6 +220,15 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, { this._close(); }, + @action + handleShowUser(user, event) { + event?.preventDefault(); + // Invokes `showUser` argument. Convert to `this.args.showUser` when + // refactoring this to a glimmer component. + this.showUser(user); + this._close(); + }, + actions: { close() { this._close(); @@ -247,9 +256,8 @@ export default Component.extend(CardContentsBase, CanCheckEmails, CleansUp, { this._close(); }, - showUser(username) { - this.showUser(username); - this._close(); + showUser(user) { + this.handleShowUser(user); }, checkEmail(user) { diff --git a/app/assets/javascripts/discourse/app/controllers/auth-token.js b/app/assets/javascripts/discourse/app/controllers/auth-token.js index 2d8c91157a3..52ddce5f4de 100644 --- a/app/assets/javascripts/discourse/app/controllers/auth-token.js +++ b/app/assets/javascripts/discourse/app/controllers/auth-token.js @@ -1,6 +1,7 @@ import Controller from "@ember/controller"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import { ajax } from "discourse/lib/ajax"; +import { action } from "@ember/object"; import { next } from "@ember/runloop"; import { userPath } from "discourse/lib/url"; @@ -17,11 +18,13 @@ export default Controller.extend(ModalFunctionality, { }); }, - actions: { - toggleExpanded() { - this.set("expanded", !this.expanded); - }, + @action + toggleExpanded(event) { + event?.preventDefault(); + this.set("expanded", !this.expanded); + }, + actions: { highlightSecure() { this.send("closeModal"); diff --git a/app/assets/javascripts/discourse/app/controllers/avatar-selector.js b/app/assets/javascripts/discourse/app/controllers/avatar-selector.js index 8373b7a106d..d63f464c2a6 100644 --- a/app/assets/javascripts/discourse/app/controllers/avatar-selector.js +++ b/app/assets/javascripts/discourse/app/controllers/avatar-selector.js @@ -1,4 +1,5 @@ import Controller from "@ember/controller"; +import { action } from "@ember/object"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import { ajax } from "discourse/lib/ajax"; import { allowsImages } from "discourse/lib/uploads"; @@ -132,6 +133,15 @@ export default Controller.extend(ModalFunctionality, { ); }, + @action + selectAvatar(url, event) { + event?.preventDefault(); + this.user + .selectAvatar(url) + .then(() => window.location.reload()) + .catch(popupAjaxError); + }, + actions: { uploadComplete() { this.set("selected", "custom"); @@ -159,13 +169,6 @@ export default Controller.extend(ModalFunctionality, { .finally(() => this.set("gravatarRefreshDisabled", false)); }, - selectAvatar(url) { - this.user - .selectAvatar(url) - .then(() => window.location.reload()) - .catch(popupAjaxError); - }, - saveAvatarSelection() { const selectedUploadId = this.selectedUploadId; const type = this.selected; diff --git a/app/assets/javascripts/discourse/app/controllers/composer.js b/app/assets/javascripts/discourse/app/controllers/composer.js index 3463f6f1d77..61a986d64f4 100644 --- a/app/assets/javascripts/discourse/app/controllers/composer.js +++ b/app/assets/javascripts/discourse/app/controllers/composer.js @@ -508,11 +508,32 @@ export default Controller.extend({ this.set("model.showFullScreenExitPrompt", false); }, - actions: { - togglePreview() { - this.toggleProperty("showPreview"); - }, + @action + async cancel(event) { + event?.preventDefault(); + await this.cancelComposer(); + }, + @action + cancelUpload(event) { + event?.preventDefault(); + this.set("model.uploadCancelled", true); + }, + + @action + togglePreview(event) { + event?.preventDefault(); + this.toggleProperty("showPreview"); + }, + + @action + viewNewReply(event) { + event?.preventDefault(); + DiscourseURL.routeTo(this.get("model.createdPost.url")); + this.close(); + }, + + actions: { closeComposer() { this.close(); }, @@ -543,10 +564,6 @@ export default Controller.extend({ }); }, - cancelUpload() { - this.set("model.uploadCancelled", true); - }, - onPopupMenuAction(menuAction) { this.send(menuAction); }, @@ -707,10 +724,6 @@ export default Controller.extend({ this.set("model.loading", false); }, - async cancel() { - await this.cancelComposer(); - }, - save(ignore, event) { this.save(false, { jump: @@ -1275,12 +1288,6 @@ export default Controller.extend({ } }, - viewNewReply() { - DiscourseURL.routeTo(this.get("model.createdPost.url")); - this.close(); - return false; - }, - async destroyDraft(draftSequence = null) { const key = this.get("model.draftKey"); if (!key) { diff --git a/app/assets/javascripts/discourse/app/controllers/discovery/categories.js b/app/assets/javascripts/discourse/app/controllers/discovery/categories.js index c46f41c1c82..1e07f67cb99 100644 --- a/app/assets/javascripts/discourse/app/controllers/discovery/categories.js +++ b/app/assets/javascripts/discourse/app/controllers/discovery/categories.js @@ -1,5 +1,6 @@ import DiscoveryController from "discourse/controllers/discovery"; import { inject as controller } from "@ember/controller"; +import { action } from "@ember/object"; import { dasherize } from "@ember/string"; import discourseComputed from "discourse-common/utils/decorators"; import { reads } from "@ember/object/computed"; @@ -50,17 +51,19 @@ export default DiscoveryController.extend({ : style; return dasherize(componentName); }, + + @action + showInserted(event) { + event?.preventDefault(); + const tracker = this.topicTrackingState; + // Move inserted into topics + this.model.loadBefore(tracker.get("newIncoming"), true); + tracker.resetTracking(); + }, + actions: { refresh() { this.send("triggerRefresh"); }, - showInserted() { - const tracker = this.topicTrackingState; - - // Move inserted into topics - this.model.loadBefore(tracker.get("newIncoming"), true); - tracker.resetTracking(); - return false; - }, }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js index a0037f4da57..3e90fd42a86 100644 --- a/app/assets/javascripts/discourse/app/controllers/discovery/topics.js +++ b/app/assets/javascripts/discourse/app/controllers/discovery/topics.js @@ -63,6 +63,17 @@ const controllerOpts = { return this._isFilterPage(filter, "new") && topicsLength > 0; }, + // Show newly inserted topics + @action + showInserted(event) { + event?.preventDefault(); + const tracker = this.topicTrackingState; + + // Move inserted into topics + this.model.loadBefore(tracker.get("newIncoming"), true); + tracker.resetTracking(); + }, + actions: { changeSort() { deprecated( @@ -72,16 +83,6 @@ const controllerOpts = { return routeAction("changeSort", this.router._router, ...arguments)(); }, - // Show newly inserted topics - showInserted() { - const tracker = this.topicTrackingState; - - // Move inserted into topics - this.model.loadBefore(tracker.get("newIncoming"), true); - tracker.resetTracking(); - return false; - }, - refresh(options = { skipResettingParams: [] }) { const filter = this.get("model.filter"); this.send("resetParams", options.skipResettingParams); diff --git a/app/assets/javascripts/discourse/app/controllers/full-page-search.js b/app/assets/javascripts/discourse/app/controllers/full-page-search.js index e082ca9d29b..a7cf017c6aa 100644 --- a/app/assets/javascripts/discourse/app/controllers/full-page-search.js +++ b/app/assets/javascripts/discourse/app/controllers/full-page-search.js @@ -14,6 +14,7 @@ import I18n from "I18n"; import { ajax } from "discourse/lib/ajax"; import { escapeExpression } from "discourse/lib/utilities"; import { isEmpty } from "@ember/utils"; +import { action } from "@ember/object"; import { gt, or } from "@ember/object/computed"; import { scrollTop } from "discourse/mixins/scroll-top"; import { setTransient } from "discourse/lib/page-tracker"; @@ -391,22 +392,24 @@ export default Controller.extend({ } }, - actions: { - createTopic(searchTerm) { - let topicCategory; - if (searchTerm.includes("category:")) { - const match = searchTerm.match(/category:(\S*)/); - if (match && match[1]) { - topicCategory = match[1]; - } + @action + createTopic(searchTerm, event) { + event?.preventDefault(); + let topicCategory; + if (searchTerm.includes("category:")) { + const match = searchTerm.match(/category:(\S*)/); + if (match && match[1]) { + topicCategory = match[1]; } - this.composer.open({ - action: Composer.CREATE_TOPIC, - draftKey: Composer.NEW_TOPIC_KEY, - topicCategory, - }); - }, + } + this.composer.open({ + action: Composer.CREATE_TOPIC, + draftKey: Composer.NEW_TOPIC_KEY, + topicCategory, + }); + }, + actions: { selectAll() { this.selected.addObjects(this.get("model.posts").mapBy("topic")); diff --git a/app/assets/javascripts/discourse/app/controllers/history.js b/app/assets/javascripts/discourse/app/controllers/history.js index 2540c78b54b..00e64f86da3 100644 --- a/app/assets/javascripts/discourse/app/controllers/history.js +++ b/app/assets/javascripts/discourse/app/controllers/history.js @@ -1,3 +1,4 @@ +import { action } from "@ember/object"; import { alias, equal, gt, not, or } from "@ember/object/computed"; import discourseComputed, { observes, @@ -313,6 +314,24 @@ export default Controller.extend(ModalFunctionality, { } }, + @action + displayInline(event) { + event?.preventDefault(); + this.set("viewMode", "inline"); + }, + + @action + displaySideBySide(event) { + event?.preventDefault(); + this.set("viewMode", "side_by_side"); + }, + + @action + displaySideBySideMarkdown(event) { + event?.preventDefault(); + this.set("viewMode", "side_by_side_markdown"); + }, + actions: { loadFirstVersion() { this.refresh(this.get("model.post_id"), this.get("model.first_revision")); @@ -345,15 +364,5 @@ export default Controller.extend(ModalFunctionality, { revertToVersion() { this.revert(this.post, this.get("model.current_revision")); }, - - displayInline() { - this.set("viewMode", "inline"); - }, - displaySideBySide() { - this.set("viewMode", "side_by_side"); - }, - displaySideBySideMarkdown() { - this.set("viewMode", "side_by_side_markdown"); - }, }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/login.js b/app/assets/javascripts/discourse/app/controllers/login.js index 1a5a4ecc7f8..401c85f354a 100644 --- a/app/assets/javascripts/discourse/app/controllers/login.js +++ b/app/assets/javascripts/discourse/app/controllers/login.js @@ -3,7 +3,7 @@ import { alias, not, or, readOnly } from "@ember/object/computed"; import { areCookiesEnabled, escapeExpression } from "discourse/lib/utilities"; import cookie, { removeCookie } from "discourse/lib/cookie"; import { next, schedule } from "@ember/runloop"; -import EmberObject from "@ember/object"; +import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import { SECOND_FACTOR_METHODS } from "discourse/models/user"; @@ -133,7 +133,66 @@ export default Controller.extend(ModalFunctionality, { return canLoginLocalWithEmail; }, + @action + emailLogin(event) { + event?.preventDefault(); + + if (this.processingEmailLink) { + return; + } + + if (isEmpty(this.loginName)) { + this.flash(I18n.t("login.blank_username"), "info"); + return; + } + + this.set("processingEmailLink", true); + + ajax("/u/email-login", { + data: { login: this.loginName.trim() }, + type: "POST", + }) + .then((data) => { + const loginName = escapeExpression(this.loginName); + const isEmail = loginName.match(/@/); + let key = `email_login.complete_${isEmail ? "email" : "username"}`; + if (data.user_found === false) { + this.flash( + I18n.t(`${key}_not_found`, { + email: loginName, + username: loginName, + }), + "error" + ); + } else { + let postfix = data.hide_taken ? "" : "_found"; + this.flash( + I18n.t(`${key}${postfix}`, { + email: loginName, + username: loginName, + }) + ); + } + }) + .catch((e) => this.flash(extractError(e), "error")) + .finally(() => this.set("processingEmailLink", false)); + }, + + @action + handleForgotPassword(event) { + event?.preventDefault(); + const forgotPasswordController = this.forgotPassword; + if (forgotPasswordController) { + forgotPasswordController.set("accountEmailOrUsername", this.loginName); + } + this.send("showForgotPassword"); + }, + actions: { + forgotPassword() { + this.handleForgotPassword(); + }, + login() { if (this.loginDisabled) { return; @@ -297,56 +356,6 @@ export default Controller.extend(ModalFunctionality, { this.send("showCreateAccount"); }, - forgotPassword() { - const forgotPasswordController = this.forgotPassword; - if (forgotPasswordController) { - forgotPasswordController.set("accountEmailOrUsername", this.loginName); - } - this.send("showForgotPassword"); - }, - - emailLogin() { - if (this.processingEmailLink) { - return; - } - - if (isEmpty(this.loginName)) { - this.flash(I18n.t("login.blank_username"), "info"); - return; - } - - this.set("processingEmailLink", true); - - ajax("/u/email-login", { - data: { login: this.loginName.trim() }, - type: "POST", - }) - .then((data) => { - const loginName = escapeExpression(this.loginName); - const isEmail = loginName.match(/@/); - let key = `email_login.complete_${isEmail ? "email" : "username"}`; - if (data.user_found === false) { - this.flash( - I18n.t(`${key}_not_found`, { - email: loginName, - username: loginName, - }), - "error" - ); - } else { - let postfix = data.hide_taken ? "" : "_found"; - this.flash( - I18n.t(`${key}${postfix}`, { - email: loginName, - username: loginName, - }) - ); - } - }) - .catch((e) => this.flash(extractError(e), "error")) - .finally(() => this.set("processingEmailLink", false)); - }, - authenticateSecurityKey() { getWebauthnCredential( this.securityKeyChallenge, diff --git a/app/assets/javascripts/discourse/app/controllers/password-reset.js b/app/assets/javascripts/discourse/app/controllers/password-reset.js index bae85083a88..0787a28271c 100644 --- a/app/assets/javascripts/discourse/app/controllers/password-reset.js +++ b/app/assets/javascripts/discourse/app/controllers/password-reset.js @@ -1,4 +1,5 @@ import DiscourseURL, { userPath } from "discourse/lib/url"; +import { action } from "@ember/object"; import { alias, or, readOnly } from "@ember/object/computed"; import Controller from "@ember/controller"; import I18n from "I18n"; @@ -46,6 +47,13 @@ export default Controller.extend(PasswordValidation, { lockImageUrl: getURL("/images/lock.svg"), + @action + done(event) { + event?.preventDefault(); + this.set("redirected", true); + DiscourseURL.redirectTo(this.redirectTo || "/"); + }, + actions: { submit() { ajax({ @@ -126,10 +134,5 @@ export default Controller.extend(PasswordValidation, { } ); }, - - done() { - this.set("redirected", true); - DiscourseURL.redirectTo(this.redirectTo || "/"); - }, }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/account.js b/app/assets/javascripts/discourse/app/controllers/preferences/account.js index 6c3051d4522..94ba197cfdb 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/account.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/account.js @@ -2,7 +2,7 @@ import { gt, not, or } from "@ember/object/computed"; import { propertyNotEqual, setting } from "discourse/lib/computed"; import CanCheckEmails from "discourse/mixins/can-check-emails"; import Controller from "@ember/controller"; -import EmberObject from "@ember/object"; +import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; import discourseComputed from "discourse-common/utils/decorators"; import { findAll } from "discourse/models/login-method"; @@ -132,6 +132,20 @@ export default Controller.extend(CanCheckEmails, { return findAll().length > 0; }, + @action + resendConfirmationEmail(email, event) { + event?.preventDefault(); + email.set("resending", true); + this.model + .addEmail(email.email) + .then(() => { + email.set("resent", true); + }) + .finally(() => { + email.set("resending", false); + }); + }, + actions: { save() { this.set("saved", false); @@ -157,18 +171,6 @@ export default Controller.extend(CanCheckEmails, { this.model.destroyEmail(email); }, - resendConfirmationEmail(email) { - email.set("resending", true); - this.model - .addEmail(email.email) - .then(() => { - email.set("resent", true); - }) - .finally(() => { - email.set("resending", false); - }); - }, - delete() { this.dialog.alert({ message: I18n.t("user.delete_account_confirm"), diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js b/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js index 946903a5684..fc5293054a6 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js @@ -3,6 +3,7 @@ import CanCheckEmails from "discourse/mixins/can-check-emails"; import Controller from "@ember/controller"; import I18n from "I18n"; import { SECOND_FACTOR_METHODS } from "discourse/models/user"; +import { action } from "@ember/object"; import { alias } from "@ember/object/computed"; import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; @@ -91,6 +92,27 @@ export default Controller.extend(CanCheckEmails, { this.set("dirty", true); }, + @action + resetPassword(event) { + event?.preventDefault(); + + this.setProperties({ + resetPasswordLoading: true, + resetPasswordProgress: "", + }); + + return this.model + .changePassword() + .then(() => { + this.set( + "resetPasswordProgress", + I18n.t("user.change_password.success") + ); + }) + .catch(popupAjaxError) + .finally(() => this.set("resetPasswordLoading", false)); + }, + actions: { confirmPassword() { if (!this.password) { @@ -101,24 +123,6 @@ export default Controller.extend(CanCheckEmails, { this.set("password", null); }, - resetPassword() { - this.setProperties({ - resetPasswordLoading: true, - resetPasswordProgress: "", - }); - - return this.model - .changePassword() - .then(() => { - this.set( - "resetPasswordProgress", - I18n.t("user.change_password.success") - ); - }) - .catch(popupAjaxError) - .finally(() => this.set("resetPasswordLoading", false)); - }, - disableAllSecondFactors() { if (this.loading) { return; diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/security.js b/app/assets/javascripts/discourse/app/controllers/preferences/security.js index c1e7dc52c0b..800d597c651 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/security.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/security.js @@ -1,4 +1,5 @@ import Controller from "@ember/controller"; +import { action } from "@ember/object"; import { gt } from "@ember/object/computed"; import discourseComputed from "discourse-common/utils/decorators"; import { ajax } from "discourse/lib/ajax"; @@ -51,6 +52,56 @@ export default Controller.extend(CanCheckEmails, { DEFAULT_AUTH_TOKENS_COUNT ), + @action + changePassword(event) { + event?.preventDefault(); + if (!this.passwordProgress) { + this.set("passwordProgress", I18n.t("user.change_password.in_progress")); + return this.model + .changePassword() + .then(() => { + // password changed + this.setProperties({ + changePasswordProgress: false, + passwordProgress: I18n.t("user.change_password.success"), + }); + }) + .catch(() => { + // password failed to change + this.setProperties({ + changePasswordProgress: false, + passwordProgress: I18n.t("user.change_password.error"), + }); + }); + } + }, + + @action + toggleShowAllAuthTokens(event) { + event?.preventDefault(); + this.toggleProperty("showAllAuthTokens"); + }, + + @action + revokeAuthToken(token, event) { + event?.preventDefault(); + ajax( + userPath( + `${this.get("model.username_lower")}/preferences/revoke-auth-token` + ), + { + type: "POST", + data: token ? { token_id: token.id } : {}, + } + ) + .then(() => { + if (!token) { + logout(); + } // All sessions revoked + }) + .catch(popupAjaxError); + }, + actions: { save() { this.set("saved", false); @@ -60,53 +111,6 @@ export default Controller.extend(CanCheckEmails, { .catch(popupAjaxError); }, - changePassword() { - if (!this.passwordProgress) { - this.set( - "passwordProgress", - I18n.t("user.change_password.in_progress") - ); - return this.model - .changePassword() - .then(() => { - // password changed - this.setProperties({ - changePasswordProgress: false, - passwordProgress: I18n.t("user.change_password.success"), - }); - }) - .catch(() => { - // password failed to change - this.setProperties({ - changePasswordProgress: false, - passwordProgress: I18n.t("user.change_password.error"), - }); - }); - } - }, - - toggleShowAllAuthTokens() { - this.toggleProperty("showAllAuthTokens"); - }, - - revokeAuthToken(token) { - ajax( - userPath( - `${this.get("model.username_lower")}/preferences/revoke-auth-token` - ), - { - type: "POST", - data: token ? { token_id: token.id } : {}, - } - ) - .then(() => { - if (!token) { - logout(); - } // All sessions revoked - }) - .catch(popupAjaxError); - }, - showToken(token) { showModal("auth-token", { model: token }); }, diff --git a/app/assets/javascripts/discourse/app/controllers/second-factor-add-totp.js b/app/assets/javascripts/discourse/app/controllers/second-factor-add-totp.js index bdc1f3511d5..2ed9127535e 100644 --- a/app/assets/javascripts/discourse/app/controllers/second-factor-add-totp.js +++ b/app/assets/javascripts/discourse/app/controllers/second-factor-add-totp.js @@ -1,4 +1,5 @@ import Controller from "@ember/controller"; +import { action } from "@ember/object"; import I18n from "I18n"; import ModalFunctionality from "discourse/mixins/modal-functionality"; @@ -40,9 +41,15 @@ export default Controller.extend(ModalFunctionality, { .finally(() => this.set("loading", false)); }, + @action + enableShowSecondFactorKey(event) { + event?.preventDefault(); + this.set("showSecondFactorKey", true); + }, + actions: { showSecondFactorKey() { - this.set("showSecondFactorKey", true); + this.enableShowSecondFactorKey(); }, enableSecondFactor() { diff --git a/app/assets/javascripts/discourse/app/controllers/second-factor-auth.js b/app/assets/javascripts/discourse/app/controllers/second-factor-auth.js index 8e88afdf95d..f4199f9ee9b 100644 --- a/app/assets/javascripts/discourse/app/controllers/second-factor-auth.js +++ b/app/assets/javascripts/discourse/app/controllers/second-factor-auth.js @@ -212,7 +212,8 @@ export default Controller.extend({ }, @action - useAnotherMethod(newMethod) { + useAnotherMethod(newMethod, event) { + event?.preventDefault(); this.set("userSelectedMethod", newMethod); }, diff --git a/app/assets/javascripts/discourse/app/controllers/tag-show.js b/app/assets/javascripts/discourse/app/controllers/tag-show.js index 619c5e860df..2f9c4935165 100644 --- a/app/assets/javascripts/discourse/app/controllers/tag-show.js +++ b/app/assets/javascripts/discourse/app/controllers/tag-show.js @@ -110,7 +110,8 @@ export default DiscoverySortableController.extend( }, @action - showInserted() { + showInserted(event) { + event?.preventDefault(); const tracker = this.topicTrackingState; this.list.loadBefore(tracker.newIncoming, true); tracker.resetTracking(); diff --git a/app/assets/javascripts/discourse/app/controllers/tags-index.js b/app/assets/javascripts/discourse/app/controllers/tags-index.js index ec56d24003a..eff2f1d30bc 100644 --- a/app/assets/javascripts/discourse/app/controllers/tags-index.js +++ b/app/assets/javascripts/discourse/app/controllers/tags-index.js @@ -1,3 +1,4 @@ +import { action } from "@ember/object"; import { alias, notEmpty } from "@ember/object/computed"; import Controller from "@ember/controller"; import I18n from "I18n"; @@ -41,23 +42,27 @@ export default Controller.extend({ }; }, + @action + sortByCount(event) { + event?.preventDefault(); + this.setProperties({ + sortProperties: ["totalCount:desc", "id"], + sortedByCount: true, + sortedByName: false, + }); + }, + + @action + sortById(event) { + event?.preventDefault(); + this.setProperties({ + sortProperties: ["id"], + sortedByCount: false, + sortedByName: true, + }); + }, + actions: { - sortByCount() { - this.setProperties({ - sortProperties: ["totalCount:desc", "id"], - sortedByCount: true, - sortedByName: false, - }); - }, - - sortById() { - this.setProperties({ - sortProperties: ["id"], - sortedByCount: false, - sortedByName: true, - }); - }, - showUploader() { showModal("tag-upload"); }, diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index aace5530cbe..913362255e1 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -313,6 +313,55 @@ export default Controller.extend(bufferedProperty("model"), { }); }, + @action + editTopic(event) { + event?.preventDefault(); + if (this.get("model.details.can_edit")) { + this.set("editingTopic", true); + } + }, + + @action + jumpTop(event) { + event?.preventDefault(); + DiscourseURL.routeTo(this.get("model.firstPostUrl"), { + skipIfOnScreen: false, + keepFilter: true, + }); + }, + + @action + removeFeaturedLink(event) { + event?.preventDefault(); + this.set("buffered.featured_link", null); + }, + + @action + selectAll(event) { + event?.preventDefault(); + const smallActionsPostIds = this._smallActionPostIds(); + this.set("selectedPostIds", [ + ...this.get("model.postStream.stream").filter( + (postId) => !smallActionsPostIds.has(postId) + ), + ]); + this._forceRefreshPostStream(); + }, + + @action + deselectAll(event) { + event?.preventDefault(); + this.set("selectedPostIds", []); + this._forceRefreshPostStream(); + }, + + @action + toggleMultiSelect(event) { + event?.preventDefault(); + this.toggleProperty("multiSelect"); + this._forceRefreshPostStream(); + }, + actions: { topicCategoryChanged(categoryId) { this.set("buffered.category_id", categoryId); @@ -822,13 +871,6 @@ export default Controller.extend(bufferedProperty("model"), { this._jumpToPostNumber(postNumber); }, - jumpTop() { - DiscourseURL.routeTo(this.get("model.firstPostUrl"), { - skipIfOnScreen: false, - keepFilter: true, - }); - }, - jumpBottom() { // When a topic only has one lengthy post const jumpEnd = this.model.highest_post_number === 1 ? true : false; @@ -859,26 +901,6 @@ export default Controller.extend(bufferedProperty("model"), { this._jumpToPostId(postId); }, - toggleMultiSelect() { - this.toggleProperty("multiSelect"); - this._forceRefreshPostStream(); - }, - - selectAll() { - const smallActionsPostIds = this._smallActionPostIds(); - this.set("selectedPostIds", [ - ...this.get("model.postStream.stream").filter( - (postId) => !smallActionsPostIds.has(postId) - ), - ]); - this._forceRefreshPostStream(); - }, - - deselectAll() { - this.set("selectedPostIds", []); - this._forceRefreshPostStream(); - }, - togglePostSelection(post) { const selected = this.selectedPostIds; selected.includes(post.id) @@ -973,13 +995,6 @@ export default Controller.extend(bufferedProperty("model"), { .then(() => this.updateQueryParams); }, - editTopic() { - if (this.get("model.details.can_edit")) { - this.set("editingTopic", true); - } - return false; - }, - cancelEditingTopic() { this.set("editingTopic", false); this.rollbackBuffer(); @@ -1159,10 +1174,6 @@ export default Controller.extend(bufferedProperty("model"), { .catch(popupAjaxError); }, - removeFeaturedLink() { - this.set("buffered.featured_link", null); - }, - resetBumpDate() { this.model.resetBumpDate(); }, diff --git a/app/assets/javascripts/discourse/app/controllers/user-private-messages-tags.js b/app/assets/javascripts/discourse/app/controllers/user-private-messages-tags.js index 9d4f5368660..b73149358f3 100644 --- a/app/assets/javascripts/discourse/app/controllers/user-private-messages-tags.js +++ b/app/assets/javascripts/discourse/app/controllers/user-private-messages-tags.js @@ -1,25 +1,29 @@ import Controller from "@ember/controller"; +import { action } from "@ember/object"; + export default Controller.extend({ sortProperties: ["count:desc", "id"], tagsForUser: null, sortedByCount: true, sortedByName: false, - actions: { - sortByCount() { - this.setProperties({ - sortProperties: ["count:desc", "id"], - sortedByCount: true, - sortedByName: false, - }); - }, + @action + sortByCount(event) { + event?.preventDefault(); + this.setProperties({ + sortProperties: ["count:desc", "id"], + sortedByCount: true, + sortedByName: false, + }); + }, - sortById() { - this.setProperties({ - sortProperties: ["id"], - sortedByCount: false, - sortedByName: true, - }); - }, + @action + sortById(event) { + event?.preventDefault(); + this.setProperties({ + sortProperties: ["id"], + sortedByCount: false, + sortedByName: true, + }); }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/user-topics-list.js b/app/assets/javascripts/discourse/app/controllers/user-topics-list.js index 021502f615e..a40ab97f4e9 100644 --- a/app/assets/javascripts/discourse/app/controllers/user-topics-list.js +++ b/app/assets/javascripts/discourse/app/controllers/user-topics-list.js @@ -81,10 +81,10 @@ export default Controller.extend(BulkTopicSelection, { }, @action - showInserted() { + showInserted(event) { + event?.preventDefault(); this.model.loadBefore(this.pmTopicTrackingState.newIncoming); this.pmTopicTrackingState.resetIncomingTracking(); - return false; }, @action diff --git a/app/assets/javascripts/discourse/app/controllers/user.js b/app/assets/javascripts/discourse/app/controllers/user.js index 4c8ba40cbca..8f2d24beb1b 100644 --- a/app/assets/javascripts/discourse/app/controllers/user.js +++ b/app/assets/javascripts/discourse/app/controllers/user.js @@ -211,6 +211,15 @@ export default Controller.extend(CanCheckEmails, { } }, + @action + showSuspensions(event) { + event?.preventDefault(); + this.adminTools.showActionLogs(this, { + target_user: this.get("model.username"), + action_name: "suspend_user", + }); + }, + actions: { collapseProfile() { this.set("forceExpand", false); @@ -220,13 +229,6 @@ export default Controller.extend(CanCheckEmails, { this.set("forceExpand", true); }, - showSuspensions() { - this.adminTools.showActionLogs(this, { - target_user: this.get("model.username"), - action_name: "suspend_user", - }); - }, - adminDelete() { const userId = this.get("model.id"); const location = document.location.pathname; diff --git a/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs b/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs index 338e4b1cf50..6f3be0d4a92 100644 --- a/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/categories-only.hbs @@ -20,7 +20,7 @@ {{#if this.mutedCategories}}
    - +

    {{i18n "categories.muted"}}

    {{#if this.mutedToggleIcon}} {{d-icon this.mutedToggleIcon}} diff --git a/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs index 16e062fda2d..4acbfc6298f 100644 --- a/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/category-permission-row.hbs @@ -1,6 +1,6 @@ {{this.group_name}} -
    + {{d-icon "far-trash-alt"}} diff --git a/app/assets/javascripts/discourse/app/templates/components/choose-message.hbs b/app/assets/javascripts/discourse/app/templates/components/choose-message.hbs index ce6c36f17d3..c7d07e87f2e 100644 --- a/app/assets/javascripts/discourse/app/templates/components/choose-message.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/choose-message.hbs @@ -11,7 +11,7 @@ {{#each this.messages as |m|}}