From 86ecb6c58b112e598a55470b58360270c114d4cb Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Wed, 14 Sep 2022 11:06:56 -0400 Subject: [PATCH] DEV: update yes/no confirmation dialogs (#18181) --- .../addon/components/admin-web-hook-event.js | 37 ++-- .../addon/components/email-styles-editor.js | 27 ++- .../admin/addon/components/ip-lookup.js | 46 ++--- .../addon/controllers/admin-backups-index.js | 24 ++- .../addon/controllers/admin-badges/show.js | 35 ++-- .../admin-customize-colors-show.js | 25 ++- .../admin-customize-themes-show.js | 54 ++--- .../admin-logs-screened-ip-addresses.js | 53 +++-- .../addon/controllers/admin-permalinks.js | 39 ++-- .../addon/controllers/admin-user-badges.js | 23 +-- .../controllers/admin-watched-words-action.js | 33 ++-- .../addon/controllers/admin-web-hooks-show.js | 46 ++--- .../addon/controllers/admin-web-hooks.js | 43 ++-- .../admin/addon/routes/admin-backups.js | 117 +++++------ .../app/components/group-membership-button.js | 18 +- .../app/components/tag-groups-form.js | 20 +- .../discourse/app/components/user-stream.js | 38 ++-- .../app/controllers/edit-category-tabs.js | 44 ++--- .../app/controllers/feature-topic.js | 18 +- .../discourse/app/controllers/group.js | 36 ++-- .../app/controllers/preferences/username.js | 37 ++-- .../discourse/app/controllers/topic.js | 186 ++++++++---------- .../tests/acceptance/bookmarks-test.js | 7 +- .../tests/acceptance/category-edit-test.js | 7 +- .../discourse/tests/acceptance/group-test.js | 4 +- .../acceptance/user-drafts-stream-test.js | 4 +- .../wizard/addon/components/wizard-step.js | 15 +- .../stylesheets/common/base/dialog.scss | 3 + config/locales/client.en.yml | 6 +- .../javascripts/widgets/discourse-poll.js | 78 ++++---- 30 files changed, 506 insertions(+), 617 deletions(-) diff --git a/app/assets/javascripts/admin/addon/components/admin-web-hook-event.js b/app/assets/javascripts/admin/addon/components/admin-web-hook-event.js index fbcf1594e03..dd13384a2d1 100644 --- a/app/assets/javascripts/admin/addon/components/admin-web-hook-event.js +++ b/app/assets/javascripts/admin/addon/components/admin-web-hook-event.js @@ -2,15 +2,16 @@ import { ensureJSON, plainJSON, prettyJSON } from "discourse/lib/formatter"; import Component from "@ember/component"; import I18n from "I18n"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; export default Component.extend({ tagName: "li", expandDetails: null, expandDetailsRequestKey: "request", expandDetailsResponseKey: "response", + dialog: service(), @discourseComputed("model.status") statusColorClasses(status) { @@ -52,25 +53,21 @@ export default Component.extend({ actions: { redeliver() { - return bootbox.confirm( - I18n.t("admin.web_hooks.events.redeliver_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - ajax( - `/admin/api/web_hooks/${this.get( - "model.web_hook_id" - )}/events/${this.get("model.id")}/redeliver`, - { type: "POST" } - ) - .then((json) => { - this.set("model", json.web_hook_event); - }) - .catch(popupAjaxError); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.web_hooks.events.redeliver_confirm"), + didConfirm: () => { + return ajax( + `/admin/api/web_hooks/${this.get( + "model.web_hook_id" + )}/events/${this.get("model.id")}/redeliver`, + { type: "POST" } + ) + .then((json) => { + this.set("model", json.web_hook_event); + }) + .catch(popupAjaxError); + }, + }); }, toggleRequest() { diff --git a/app/assets/javascripts/admin/addon/components/email-styles-editor.js b/app/assets/javascripts/admin/addon/components/email-styles-editor.js index e3e980e6329..48621b41cf3 100644 --- a/app/assets/javascripts/admin/addon/components/email-styles-editor.js +++ b/app/assets/javascripts/admin/addon/components/email-styles-editor.js @@ -1,10 +1,11 @@ import Component from "@ember/component"; import I18n from "I18n"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { reads } from "@ember/object/computed"; +import { inject as service } from "@ember/service"; export default Component.extend({ + dialog: service(), editorId: reads("fieldName"), @discourseComputed("fieldName") @@ -33,22 +34,18 @@ export default Component.extend({ actions: { reset() { - bootbox.confirm( - I18n.t("admin.customize.email_style.reset_confirm", { + this.dialog.yesNoConfirm({ + message: I18n.t("admin.customize.email_style.reset_confirm", { fieldName: I18n.t(`admin.customize.email_style.${this.fieldName}`), }), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.styles.setField( - this.fieldName, - this.styles.get(`default_${this.fieldName}`) - ); - this.notifyPropertyChange("editorContents"); - } - } - ); + didConfirm: () => { + this.styles.setField( + this.fieldName, + this.styles.get(`default_${this.fieldName}`) + ); + this.notifyPropertyChange("editorContents"); + }, + }); }, save() { this.attrs.save(); diff --git a/app/assets/javascripts/admin/addon/components/ip-lookup.js b/app/assets/javascripts/admin/addon/components/ip-lookup.js index 920cd390699..222e04b9ab6 100644 --- a/app/assets/javascripts/admin/addon/components/ip-lookup.js +++ b/app/assets/javascripts/admin/addon/components/ip-lookup.js @@ -3,13 +3,15 @@ import Component from "@ember/component"; import EmberObject from "@ember/object"; import I18n from "I18n"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import copyText from "discourse/lib/copy-text"; import discourseComputed from "discourse-common/utils/decorators"; import discourseLater from "discourse-common/lib/later"; +import { inject as service } from "@ember/service"; +import { popupAjaxError } from "discourse/lib/ajax-error"; export default Component.extend({ classNames: ["ip-lookup"], + dialog: service(), @discourseComputed("other_accounts.length", "totalOthersWithSameIP") otherAccountsToDelete(otherAccountsLength, totalOthersWithSameIP) { @@ -89,29 +91,27 @@ export default Component.extend({ }, deleteOtherAccounts() { - bootbox.confirm( - I18n.t("ip_lookup.confirm_delete_other_accounts"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - this.setProperties({ - other_accounts: null, - otherAccountsLoading: true, - totalOthersWithSameIP: null, - }); + this.dialog.yesNoConfirm({ + message: I18n.t("ip_lookup.confirm_delete_other_accounts"), + didConfirm: () => { + this.setProperties({ + other_accounts: null, + otherAccountsLoading: true, + totalOthersWithSameIP: null, + }); - ajax("/admin/users/delete-others-with-same-ip.json", { - type: "DELETE", - data: { - ip: this.ip, - exclude: this.userId, - order: "trust_level DESC", - }, - }).then(() => this.send("lookup")); - } - } - ); + ajax("/admin/users/delete-others-with-same-ip.json", { + type: "DELETE", + data: { + ip: this.ip, + exclude: this.userId, + order: "trust_level DESC", + }, + }) + .catch(popupAjaxError) + .finally(this.send("lookup")); + }, + }); }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-backups-index.js b/app/assets/javascripts/admin/addon/controllers/admin-backups-index.js index c53adeecf9a..72a1bd18a54 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-backups-index.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-backups-index.js @@ -3,11 +3,13 @@ import { alias, equal } from "@ember/object/computed"; import { i18n, setting } from "discourse/lib/computed"; import I18n from "I18n"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; + import discourseComputed from "discourse-common/utils/decorators"; +import { inject as service } from "@ember/service"; export default Controller.extend({ adminBackups: controller(), + dialog: service(), status: alias("adminBackups.model"), uploadLabel: i18n("admin.backups.upload.label"), backupLocation: setting("backup_location"), @@ -27,17 +29,13 @@ export default Controller.extend({ actions: { toggleReadOnlyMode() { if (!this.site.get("isReadOnly")) { - bootbox.confirm( - I18n.t("admin.backups.read_only.enable.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - this.set("currentUser.hideReadOnlyAlert", true); - this._toggleReadOnlyMode(true); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("admin.backups.read_only.enable.confirm"), + didConfirm: () => { + this.set("currentUser.hideReadOnlyAlert", true); + this._toggleReadOnlyMode(true); + }, + }); } else { this._toggleReadOnlyMode(false); } @@ -46,7 +44,7 @@ export default Controller.extend({ download(backup) { const link = backup.get("filename"); ajax(`/admin/backups/${link}`, { type: "PUT" }).then(() => - bootbox.alert(I18n.t("admin.backups.operations.download.alert")) + this.dialog.alert(I18n.t("admin.backups.operations.download.alert")) ); }, }, 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 0a360789a27..2b380818269 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-badges/show.js @@ -1,7 +1,7 @@ import Controller, { inject as controller } from "@ember/controller"; import { observes } from "discourse-common/utils/decorators"; import I18n from "I18n"; -import bootbox from "bootbox"; + import { bufferedProperty } from "discourse/mixins/buffered-content"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { next } from "@ember/runloop"; @@ -18,6 +18,7 @@ export default class AdminBadgesShowController extends Controller.extend( bufferedProperty("model") ) { @service router; + @service dialog; @controller adminBadges; @tracked saving = false; @@ -216,23 +217,19 @@ export default class AdminBadgesShowController extends Controller.extend( return; } - return bootbox.confirm( - I18n.t("admin.badges.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - model - .destroy() - .then(() => { - adminBadges.removeObject(model); - this.transitionToRoute("adminBadges.index"); - }) - .catch(() => { - bootbox.alert(I18n.t("generic_error")); - }); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.badges.delete_confirm"), + didConfirm: () => { + model + .destroy() + .then(() => { + adminBadges.removeObject(model); + this.transitionToRoute("adminBadges.index"); + }) + .catch(() => { + this.dialog.alert(I18n.t("generic_error")); + }); + }, + }); } } diff --git a/app/assets/javascripts/admin/addon/controllers/admin-customize-colors-show.js b/app/assets/javascripts/admin/addon/controllers/admin-customize-colors-show.js index 80b5e465c2c..3df2ee25d4f 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-customize-colors-show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-customize-colors-show.js @@ -1,11 +1,12 @@ import Controller from "@ember/controller"; import I18n from "I18n"; -import bootbox from "bootbox"; import discourseLater from "discourse-common/lib/later"; import { action, computed } from "@ember/object"; import { clipboardCopy } from "discourse/lib/utilities"; +import { inject as service } from "@ember/service"; export default class AdminCustomizeColorsShowController extends Controller { + @service dialog; onlyOverridden = false; @computed("model.colors.[]", "onlyOverridden") @@ -73,18 +74,14 @@ export default class AdminCustomizeColorsShowController extends Controller { @action destroy() { - return bootbox.confirm( - I18n.t("admin.customize.colors.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.model.destroy().then(() => { - this.allColors.removeObject(this.model); - this.replaceRoute("adminCustomize.colors"); - }); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.customize.colors.delete_confirm"), + didConfirm: () => { + return this.model.destroy().then(() => { + this.allColors.removeObject(this.model); + this.replaceRoute("adminCustomize.colors"); + }); + }, + }); } } diff --git a/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-show.js b/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-show.js index 2418c785e0b..61ffda925e3 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-show.js @@ -16,10 +16,12 @@ import { makeArray } from "discourse-common/lib/helpers"; import { popupAjaxError } from "discourse/lib/ajax-error"; import showModal from "discourse/lib/show-modal"; import { url } from "discourse/lib/computed"; +import { inject as service } from "@ember/service"; const THEME_UPLOAD_VAR = 2; export default Controller.extend({ + dialog: service(), downloadUrl: url("model.id", "/admin/customize/themes/%@/export"), previewUrl: url("model.id", "/admin/themes/%@/preview"), addButtonDisabled: empty("selectedChildThemeId"), @@ -345,16 +347,10 @@ export default Controller.extend({ }, removeUpload(upload) { - return bootbox.confirm( - I18n.t("admin.customize.theme.delete_upload_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.model.removeField(upload); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.customize.theme.delete_upload_confirm"), + didConfirm: () => this.model.removeField(upload), + }); }, removeChildTheme(theme) { @@ -364,23 +360,19 @@ export default Controller.extend({ }, destroy() { - return bootbox.confirm( - I18n.t("admin.customize.delete_confirm", { + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.customize.delete_confirm", { theme_name: this.get("model.name"), }), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - const model = this.model; - model.setProperties({ recentlyInstalled: false }); - model.destroyRecord().then(() => { - this.allThemes.removeObject(model); - this.transitionToRoute("adminCustomizeThemes"); - }); - } - } - ); + didConfirm: () => { + const model = this.model; + model.setProperties({ recentlyInstalled: false }); + model.destroyRecord().then(() => { + this.allThemes.removeObject(model); + this.transitionToRoute("adminCustomizeThemes"); + }); + }, + }); }, switchType() { @@ -398,16 +390,10 @@ export default Controller.extend({ }); } - bootbox.confirm( + return this.dialog.yesNoConfirm({ message, - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.commitSwitchType(); - } - } - ); + didConfirm: () => this.commitSwitchType(), + }); }, enableComponent() { 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 df734004ece..0e8b43f9c13 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 @@ -2,13 +2,14 @@ import Controller from "@ember/controller"; import I18n from "I18n"; import { INPUT_DELAY } from "discourse-common/config/environment"; import ScreenedIpAddress from "admin/models/screened-ip-address"; -import bootbox from "bootbox"; 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 { inject as service } from "@ember/service"; export default Controller.extend({ + dialog: service(), loading: false, filter: null, savedIpAddress: null, @@ -59,13 +60,13 @@ export default Controller.extend({ .then(() => this.set("savedIpAddress", null)) .catch((e) => { if (e.jqXHR.responseJSON && e.jqXHR.responseJSON.errors) { - bootbox.alert( + this.dialog.alert( I18n.t("generic_error_with_reason", { error: e.jqXHR.responseJSON.errors.join(". "), }) ); } else { - bootbox.alert(I18n.t("generic_error")); + this.dialog.alert(I18n.t("generic_error")); } if (wasEditing) { record.set("editing", true); @@ -74,33 +75,29 @@ export default Controller.extend({ }, destroy(record) { - return bootbox.confirm( - I18n.t("admin.logs.screened_ips.delete_confirm", { + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.logs.screened_ips.delete_confirm", { ip_address: record.get("ip_address"), }), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - record - .destroy() - .then((deleted) => { - if (deleted) { - this.model.removeObject(record); - } else { - bootbox.alert(I18n.t("generic_error")); - } - }) - .catch((e) => { - bootbox.alert( - I18n.t("generic_error_with_reason", { - error: `http: ${e.status} - ${e.body}`, - }) - ); - }); - } - } - ); + didConfirm: () => { + return record + .destroy() + .then((deleted) => { + if (deleted) { + this.model.removeObject(record); + } else { + this.dialog.alert(I18n.t("generic_error")); + } + }) + .catch((e) => { + this.dialog.alert( + I18n.t("generic_error_with_reason", { + error: `http: ${e.status} - ${e.body}`, + }) + ); + }); + }, + }); }, recordAdded(arg) { diff --git a/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js b/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js index 9993a19fe5f..07372d2f104 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-permalinks.js @@ -2,12 +2,13 @@ import Controller from "@ember/controller"; import I18n from "I18n"; import { INPUT_DELAY } from "discourse-common/config/environment"; import Permalink from "admin/models/permalink"; -import bootbox from "bootbox"; import discourseDebounce from "discourse-common/lib/debounce"; import { observes } from "discourse-common/utils/decorators"; import { clipboardCopy } from "discourse/lib/utilities"; +import { inject as service } from "@ember/service"; export default Controller.extend({ + dialog: service(), loading: false, filter: null, @@ -34,27 +35,23 @@ export default Controller.extend({ }, destroy(record) { - return bootbox.confirm( - I18n.t("admin.permalink.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - record.destroy().then( - (deleted) => { - if (deleted) { - this.model.removeObject(record); - } else { - bootbox.alert(I18n.t("generic_error")); - } - }, - function () { - bootbox.alert(I18n.t("generic_error")); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.permalink.delete_confirm"), + didConfirm: () => { + return record.destroy().then( + (deleted) => { + if (deleted) { + this.model.removeObject(record); + } else { + this.dialog.alert(I18n.t("generic_error")); } - ); - } - } - ); + }, + function () { + this.dialog.alert(I18n.t("generic_error")); + } + ); + }, + }); }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-user-badges.js b/app/assets/javascripts/admin/addon/controllers/admin-user-badges.js index 9461374085c..b36a22af0ce 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-user-badges.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-user-badges.js @@ -2,13 +2,14 @@ import Controller, { inject as controller } from "@ember/controller"; import { alias, sort } from "@ember/object/computed"; import GrantBadgeController from "discourse/mixins/grant-badge-controller"; import I18n from "I18n"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { next } from "@ember/runloop"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; export default Controller.extend(GrantBadgeController, { adminUser: controller(), + dialog: service(), user: alias("adminUser.model"), userBadges: alias("model"), allBadges: alias("badges"), @@ -90,18 +91,14 @@ export default Controller.extend(GrantBadgeController, { }, revokeBadge(userBadge) { - return bootbox.confirm( - I18n.t("admin.badges.revoke_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - userBadge.revoke().then(() => { - this.model.removeObject(userBadge); - }); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.badges.revoke_confirm"), + didConfirm: () => { + return userBadge.revoke().then(() => { + this.model.removeObject(userBadge); + }); + }, + }); }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-watched-words-action.js b/app/assets/javascripts/admin/addon/controllers/admin-watched-words-action.js index d84d80615dc..a1b158a6774 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-watched-words-action.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-watched-words-action.js @@ -2,16 +2,17 @@ import Controller, { inject as controller } from "@ember/controller"; import I18n from "I18n"; import WatchedWord from "admin/models/watched-word"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { fmt } from "discourse/lib/computed"; import { or } from "@ember/object/computed"; import { schedule } from "@ember/runloop"; import showModal from "discourse/lib/show-modal"; +import { inject as service } from "@ember/service"; export default Controller.extend({ adminWatchedWords: controller(), actionNameKey: null, + dialog: service(), downloadLink: fmt( "actionNameKey", "/admin/customize/watched_words/action/%@/download" @@ -93,25 +94,21 @@ export default Controller.extend({ clearAll() { const actionKey = this.actionNameKey; - bootbox.confirm( - I18n.t("admin.watched_words.clear_all_confirm", { + this.dialog.yesNoConfirm({ + message: I18n.t("admin.watched_words.clear_all_confirm", { action: I18n.t("admin.watched_words.actions." + actionKey), }), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - ajax(`/admin/customize/watched_words/action/${actionKey}.json`, { - type: "DELETE", - }).then(() => { - const action = this.findAction(actionKey); - if (action) { - action.set("words", []); - } - }); - } - } - ); + didConfirm: () => { + ajax(`/admin/customize/watched_words/action/${actionKey}.json`, { + type: "DELETE", + }).then(() => { + const action = this.findAction(actionKey); + if (action) { + action.set("words", []); + } + }); + }, + }); }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js index a952f4e5466..ba75e291991 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks-show.js @@ -2,15 +2,16 @@ import Controller, { inject as controller } from "@ember/controller"; import EmberObject from "@ember/object"; import I18n from "I18n"; import { alias } from "@ember/object/computed"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { extractDomainFromUrl } from "discourse/lib/utilities"; import { isAbsoluteURL } from "discourse-common/lib/get-url"; import { isEmpty } from "@ember/utils"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; export default Controller.extend({ adminWebHooks: controller(), + dialog: service(), eventTypes: alias("adminWebHooks.eventTypes"), defaultEventTypes: alias("adminWebHooks.defaultEventTypes"), contentTypes: alias("adminWebHooks.contentTypes"), @@ -113,39 +114,28 @@ export default Controller.extend({ domain.match(/127\.\d+\.\d+\.\d+/) || isAbsoluteURL(url) ) { - return bootbox.confirm( - I18n.t("admin.web_hooks.warn_local_payload_url"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - return saveWebHook(); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.web_hooks.warn_local_payload_url"), + didConfirm: () => saveWebHook(), + }); } return saveWebHook(); }, destroy() { - return bootbox.confirm( - I18n.t("admin.web_hooks.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - const model = this.model; - model - .destroyRecord() - .then(() => { - this.adminWebHooks.get("model").removeObject(model); - this.transitionToRoute("adminWebHooks"); - }) - .catch(popupAjaxError); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.web_hooks.delete_confirm"), + didConfirm: () => { + this.model + .destroyRecord() + .then(() => { + this.adminWebHooks.get("model").removeObject(this.model); + this.transitionToRoute("adminWebHooks"); + }) + .catch(popupAjaxError); + }, + }); }, }, }); diff --git a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks.js b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks.js index 6e1acf97dc1..405f8594132 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-web-hooks.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-web-hooks.js @@ -1,30 +1,29 @@ import Controller from "@ember/controller"; import I18n from "I18n"; -import bootbox from "bootbox"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; +import { action } from "@ember/object"; export default Controller.extend({ - actions: { - destroy(webhook) { - return bootbox.confirm( - I18n.t("admin.web_hooks.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - webhook - .destroyRecord() - .then(() => { - this.model.removeObject(webhook); - }) - .catch(popupAjaxError); - } - } - ); - }, + dialog: service(), - loadMore() { - this.model.loadMore(); - }, + @action + destroy(webhook) { + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.web_hooks.delete_confirm"), + didConfirm: () => { + webhook + .destroyRecord() + .then(() => { + this.model.removeObject(webhook); + }) + .catch(popupAjaxError); + }, + }); + }, + + @action + loadMore() { + this.model.loadMore(); }, }); diff --git a/app/assets/javascripts/admin/addon/routes/admin-backups.js b/app/assets/javascripts/admin/addon/routes/admin-backups.js index 063eeec9d77..c78ef4ca1cf 100644 --- a/app/assets/javascripts/admin/addon/routes/admin-backups.js +++ b/app/assets/javascripts/admin/addon/routes/admin-backups.js @@ -6,14 +6,15 @@ import I18n from "I18n"; import PreloadStore from "discourse/lib/preload-store"; import User from "discourse/models/user"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import { extractError } from "discourse/lib/ajax-error"; import getURL from "discourse-common/lib/get-url"; import showModal from "discourse/lib/show-modal"; - +import { inject as service } from "@ember/service"; const LOG_CHANNEL = "/admin/backups/logs"; export default DiscourseRoute.extend({ + dialog: service(), + activate() { this.messageBus.subscribe(LOG_CHANNEL, (log) => { if (log.message === "[STARTED]") { @@ -28,7 +29,7 @@ export default DiscourseRoute.extend({ "model.isOperationRunning", false ); - bootbox.alert( + this.dialog.alert( I18n.t("admin.backups.operations.failed", { operation: log.operation, }) @@ -77,88 +78,72 @@ export default DiscourseRoute.extend({ this.transitionTo("admin.backups.logs"); Backup.start(withUploads).then((result) => { if (!result.success) { - bootbox.alert(result.message); + this.dialog.alert(result.message); } }); }, destroyBackup(backup) { - bootbox.confirm( - I18n.t("admin.backups.operations.destroy.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - backup - .destroy() - .then(() => - this.controllerFor("adminBackupsIndex") - .get("model") - .removeObject(backup) - ); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.backups.operations.destroy.confirm"), + didConfirm: () => { + backup + .destroy() + .then(() => + this.controllerFor("adminBackupsIndex") + .get("model") + .removeObject(backup) + ); + }, + }); }, startRestore(backup) { - bootbox.confirm( - I18n.t("admin.backups.operations.restore.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - this.transitionTo("admin.backups.logs"); - backup.restore(); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("admin.backups.operations.restore.confirm"), + didConfirm: () => { + this.transitionTo("admin.backups.logs"); + backup.restore(); + }, + }); }, cancelOperation() { - bootbox.confirm( - I18n.t("admin.backups.operations.cancel.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - Backup.cancel().then(() => { - this.controllerFor("adminBackups").set( - "model.isOperationRunning", - false - ); - }); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("admin.backups.operations.cancel.confirm"), + didConfirm: () => { + Backup.cancel().then(() => { + this.controllerFor("adminBackups").set( + "model.isOperationRunning", + false + ); + }); + }, + }); }, rollback() { - bootbox.confirm( - I18n.t("admin.backups.operations.rollback.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - Backup.rollback().then((result) => { - if (!result.success) { - bootbox.alert(result.message); - } else { - // redirect to homepage (session might be lost) - window.location = getURL("/"); - } - }); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("admin.backups.operations.rollback.confirm"), + didConfirm: () => { + Backup.rollback().then((result) => { + if (!result.success) { + this.dialog.alert(result.message); + } else { + // redirect to homepage (session might be lost) + window.location = getURL("/"); + } + }); + }, + }); }, uploadSuccess(filename) { - bootbox.alert(I18n.t("admin.backups.upload.success", { filename })); + this.dialog.alert(I18n.t("admin.backups.upload.success", { filename })); }, uploadError(filename, message) { - bootbox.alert( + this.dialog.alert( I18n.t("admin.backups.upload.error", { filename, message }) ); }, @@ -173,7 +158,7 @@ export default DiscourseRoute.extend({ ); }) .catch((error) => { - bootbox.alert( + this.dialog.alert( I18n.t("admin.backups.backup_storage_error", { error_message: extractError(error), }) diff --git a/app/assets/javascripts/discourse/app/components/group-membership-button.js b/app/assets/javascripts/discourse/app/components/group-membership-button.js index 4c8239aedca..bad2835a199 100644 --- a/app/assets/javascripts/discourse/app/components/group-membership-button.js +++ b/app/assets/javascripts/discourse/app/components/group-membership-button.js @@ -1,13 +1,14 @@ import Component from "@ember/component"; import I18n from "I18n"; -import bootbox from "bootbox"; import cookie from "discourse/lib/cookie"; import discourseComputed from "discourse-common/utils/decorators"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; import showModal from "discourse/lib/show-modal"; export default Component.extend({ classNames: ["group-membership-button"], + dialog: service(), @discourseComputed("model.public_admission", "userIsGroupUser") canJoinGroup(publicAdmission, userIsGroupUser) { @@ -73,16 +74,11 @@ export default Component.extend({ if (this.model.public_admission) { this.removeFromGroup(); } else { - return bootbox.confirm( - I18n.t("groups.confirm_leave"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - result - ? this.removeFromGroup() - : this.set("updatingMembership", false); - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("groups.confirm_leave"), + didConfirm: () => this.removeFromGroup(), + didCancel: () => this.set("updatingMembership", false), + }); } }, diff --git a/app/assets/javascripts/discourse/app/components/tag-groups-form.js b/app/assets/javascripts/discourse/app/components/tag-groups-form.js index dde6b73ef09..532cfeb829f 100644 --- a/app/assets/javascripts/discourse/app/components/tag-groups-form.js +++ b/app/assets/javascripts/discourse/app/components/tag-groups-form.js @@ -2,7 +2,6 @@ import Component from "@ember/component"; import Group from "discourse/models/group"; import I18n from "I18n"; import PermissionType from "discourse/models/permission-type"; -import bootbox from "bootbox"; import { bufferedProperty } from "discourse/mixins/buffered-content"; import discourseComputed from "discourse-common/utils/decorators"; import { inject as service } from "@ember/service"; @@ -10,6 +9,7 @@ import { isEmpty } from "@ember/utils"; export default Component.extend(bufferedProperty("model"), { router: service(), + dialog: service(), tagName: "", allGroups: null, @@ -108,7 +108,7 @@ export default Component.extend(bufferedProperty("model"), { save() { if (this.cannotSave) { - bootbox.alert(I18n.t("tagging.groups.cannot_save")); + this.dialog.alert(I18n.t("tagging.groups.cannot_save")); return false; } @@ -140,22 +140,16 @@ export default Component.extend(bufferedProperty("model"), { }, destroy() { - return bootbox.confirm( - I18n.t("tagging.groups.confirm_delete"), - I18n.t("no_value"), - I18n.t("yes_value"), - (destroy) => { - if (!destroy) { - return; - } - + return this.dialog.yesNoConfirm({ + message: I18n.t("tagging.groups.confirm_delete"), + didConfirm: () => { this.model.destroyRecord().then(() => { if (this.onDestroy) { this.onDestroy(); } }); - } - ); + }, + }); }, }, }); diff --git a/app/assets/javascripts/discourse/app/components/user-stream.js b/app/assets/javascripts/discourse/app/components/user-stream.js index 53b636831d5..d025570b456 100644 --- a/app/assets/javascripts/discourse/app/components/user-stream.js +++ b/app/assets/javascripts/discourse/app/components/user-stream.js @@ -6,15 +6,16 @@ import I18n from "I18n"; import LoadMore from "discourse/mixins/load-more"; import Post from "discourse/models/post"; import { NEW_TOPIC_KEY } from "discourse/models/composer"; -import bootbox from "bootbox"; import { getOwner } from "discourse-common/lib/get-owner"; import { observes } from "discourse-common/utils/decorators"; import { on } from "@ember/object/evented"; import { popupAjaxError } from "discourse/lib/ajax-error"; import { next, schedule } from "@ember/runloop"; +import { inject as service } from "@ember/service"; export default Component.extend(LoadMore, { tagName: "ul", + dialog: service(), _lastDecoratedElement: null, _initialize: on("init", function () { @@ -127,25 +128,22 @@ export default Component.extend(LoadMore, { removeDraft(draft) { const stream = this.stream; - bootbox.confirm( - I18n.t("drafts.remove_confirmation"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - Draft.clear(draft.draft_key, draft.sequence) - .then(() => { - stream.remove(draft); - if (draft.draft_key === NEW_TOPIC_KEY) { - this.currentUser.set("has_topic_draft", false); - } - }) - .catch((error) => { - popupAjaxError(error); - }); - } - } - ); + + this.dialog.yesNoConfirm({ + message: I18n.t("drafts.remove_confirmation"), + didConfirm: () => { + Draft.clear(draft.draft_key, draft.sequence) + .then(() => { + stream.remove(draft); + if (draft.draft_key === NEW_TOPIC_KEY) { + this.currentUser.set("has_topic_draft", false); + } + }) + .catch((error) => { + popupAjaxError(error); + }); + }, + }); }, loadMore() { diff --git a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js index bbfa66193d9..154fd5e94e6 100644 --- a/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js +++ b/app/assets/javascripts/discourse/app/controllers/edit-category-tabs.js @@ -6,11 +6,12 @@ import DiscourseURL from "discourse/lib/url"; import I18n from "I18n"; import { NotificationLevels } from "discourse/lib/notification-levels"; import PermissionType from "discourse/models/permission-type"; -import bootbox from "bootbox"; import { extractError } from "discourse/lib/ajax-error"; import { underscore } from "@ember/string"; +import { inject as service } from "@ember/service"; export default Controller.extend({ + dialog: service(), selectedTab: "general", saving: false, deleting: false, @@ -112,35 +113,30 @@ export default Controller.extend({ } }) .catch((error) => { - bootbox.alert(extractError(error)); + this.dialog.alert(extractError(error)); this.set("saving", false); }); }, deleteCategory() { this.set("deleting", true); - bootbox.confirm( - I18n.t("category.delete_confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.model - .destroy() - .then(() => { - this.transitionToRoute("discovery.categories"); - }) - .catch(() => { - this.displayErrors([I18n.t("category.delete_error")]); - }) - .finally(() => { - this.set("deleting", false); - }); - } else { - this.set("deleting", false); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("category.delete_confirm"), + didConfirm: () => { + this.model + .destroy() + .then(() => { + this.transitionToRoute("discovery.categories"); + }) + .catch(() => { + this.displayErrors([I18n.t("category.delete_error")]); + }) + .finally(() => { + this.set("deleting", false); + }); + }, + didCancel: () => this.set("deleting", false), + }); }, toggleDeleteTooltip() { diff --git a/app/assets/javascripts/discourse/app/controllers/feature-topic.js b/app/assets/javascripts/discourse/app/controllers/feature-topic.js index f32d496c8b6..d6ac71574d6 100644 --- a/app/assets/javascripts/discourse/app/controllers/feature-topic.js +++ b/app/assets/javascripts/discourse/app/controllers/feature-topic.js @@ -3,12 +3,13 @@ import EmberObject from "@ember/object"; import I18n from "I18n"; import ModalFunctionality from "discourse/mixins/modal-functionality"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import { categoryLinkHTML } from "discourse/helpers/category-link"; import discourseComputed from "discourse-common/utils/decorators"; +import { inject as service } from "@ember/service"; export default Controller.extend(ModalFunctionality, { topicController: controller("topic"), + dialog: service(), loading: true, pinnedInCategoryCount: 0, @@ -139,19 +140,16 @@ export default Controller.extend(ModalFunctionality, { _confirmBeforePinningGlobally() { const count = this.pinnedGloballyCount; + if (count < 4) { this._forwardAction("pinGlobally"); } else { this.send("hideModal"); - bootbox.confirm( - I18n.t("topic.feature_topic.confirm_pin_globally", { count }), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => - confirmed - ? this._forwardAction("pinGlobally") - : this.send("reopenModal") - ); + this.dialog.yesNoConfirm({ + message: I18n.t("topic.feature_topic.confirm_pin_globally", { count }), + didConfirm: () => this._forwardAction("pinGlobally"), + didCancel: () => this.send("reopenModal"), + }); } }, diff --git a/app/assets/javascripts/discourse/app/controllers/group.js b/app/assets/javascripts/discourse/app/controllers/group.js index 07cda7ce065..27f074fd93e 100644 --- a/app/assets/javascripts/discourse/app/controllers/group.js +++ b/app/assets/javascripts/discourse/app/controllers/group.js @@ -1,9 +1,9 @@ import Controller, { inject as controller } from "@ember/controller"; import EmberObject, { action } from "@ember/object"; import I18n from "I18n"; -import bootbox from "bootbox"; import discourseComputed from "discourse-common/utils/decorators"; import { capitalize } from "@ember/string"; +import { inject as service } from "@ember/service"; const Tab = EmberObject.extend({ init() { @@ -18,6 +18,7 @@ const Tab = EmberObject.extend({ export default Controller.extend({ application: controller(), + dialog: service(), counts: null, showing: "members", destroying: null, @@ -140,26 +141,21 @@ export default Controller.extend({ }); } - bootbox.confirm( + this.dialog.yesNoConfirm({ message, - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - model - .destroy() - .then(() => this.transitionToRoute("groups.index")) - .catch((error) => { - // eslint-disable-next-line no-console - console.error(error); - bootbox.alert(I18n.t("admin.groups.delete_failed")); - }) - .finally(() => this.set("destroying", false)); - } else { - this.set("destroying", false); - } - } - ); + didConfirm: () => { + model + .destroy() + .then(() => this.transitionToRoute("groups.index")) + .catch((error) => { + // eslint-disable-next-line no-console + console.error(error); + this.dialog.alert(I18n.t("admin.groups.delete_failed")); + }) + .finally(() => this.set("destroying", false)); + }, + didCancel: () => this.set("destroying", false), + }); }, @action diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/username.js b/app/assets/javascripts/discourse/app/controllers/preferences/username.js index 6fbeb232c05..cb756dbb528 100644 --- a/app/assets/javascripts/discourse/app/controllers/preferences/username.js +++ b/app/assets/javascripts/discourse/app/controllers/preferences/username.js @@ -5,11 +5,12 @@ import { propertyEqual, setting } from "discourse/lib/computed"; import Controller from "@ember/controller"; import I18n from "I18n"; import User from "discourse/models/user"; -import bootbox from "bootbox"; import { isEmpty } from "@ember/utils"; import { popupAjaxError } from "discourse/lib/ajax-error"; +import { inject as service } from "@ember/service"; export default Controller.extend({ + dialog: service(), taken: false, saving: false, errorMessage: null, @@ -70,25 +71,21 @@ export default Controller.extend({ return; } - return bootbox.confirm( - I18n.t("user.change_username.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.set("saving", true); - this.model - .changeUsername(this.newUsername) - .then(() => { - DiscourseURL.redirectTo( - userPath(this.newUsername.toLowerCase() + "/preferences") - ); - }) - .catch(popupAjaxError) - .finally(() => this.set("saving", false)); - } - } - ); + return this.dialog.yesNoConfirm({ + title: I18n.t("user.change_username.confirm"), + didConfirm: () => { + this.set("saving", true); + this.model + .changeUsername(this.newUsername) + .then(() => { + DiscourseURL.redirectTo( + userPath(this.newUsername.toLowerCase() + "/preferences") + ); + }) + .catch(popupAjaxError) + .finally(() => this.set("saving", false)); + }, + }); }, }, }); diff --git a/app/assets/javascripts/discourse/app/controllers/topic.js b/app/assets/javascripts/discourse/app/controllers/topic.js index 3eee01298fe..d6604556f0b 100644 --- a/app/assets/javascripts/discourse/app/controllers/topic.js +++ b/app/assets/javascripts/discourse/app/controllers/topic.js @@ -16,7 +16,6 @@ import QuoteState from "discourse/lib/quote-state"; import Topic from "discourse/models/topic"; import TopicTimer from "discourse/models/topic-timer"; import { ajax } from "discourse/lib/ajax"; -import bootbox from "bootbox"; import { bufferedProperty } from "discourse/mixins/buffered-content"; import { buildQuote } from "discourse/lib/quote"; import { deepMerge } from "discourse-common/lib/object"; @@ -47,6 +46,7 @@ export function registerCustomPostMessageCallback(type, callback) { export default Controller.extend(bufferedProperty("model"), { composer: controller(), application: controller(), + dialog: service(), documentTitle: service(), screenTrack: service(), @@ -633,21 +633,24 @@ export default Controller.extend(bufferedProperty("model"), { const buttons = []; - buttons.push({ - label: I18n.t("cancel"), - class: "btn-danger right", - }); + const directReplyIds = replies + .filter((r) => r.level === 1) + .map((r) => r.id); buttons.push({ - label: I18n.t("post.controls.delete_replies.just_the_post"), - callback() { - post - .destroy(user, opts) + label: I18n.t("post.controls.delete_replies.direct_replies", { + count: directReplyIds.length, + }), + class: "btn-primary", + action: () => { + loadedPosts.forEach( + (p) => + (p === post || directReplyIds.includes(p.id)) && + p.setDeletedState(user) + ); + Post.deleteMany([post.id, ...directReplyIds]) .then(refresh) - .catch((error) => { - popupAjaxError(error); - post.undoDeleteState(); - }); + .catch(popupAjaxError); }, }); @@ -656,7 +659,7 @@ export default Controller.extend(bufferedProperty("model"), { label: I18n.t("post.controls.delete_replies.all_replies", { count: replies.length, }), - callback() { + action: () => { loadedPosts.forEach( (p) => (p === post || replies.some((r) => r.id === p.id)) && @@ -669,31 +672,28 @@ export default Controller.extend(bufferedProperty("model"), { }); } - const directReplyIds = replies - .filter((r) => r.level === 1) - .map((r) => r.id); - buttons.push({ - label: I18n.t("post.controls.delete_replies.direct_replies", { - count: directReplyIds.length, - }), - class: "btn-primary", - callback() { - loadedPosts.forEach( - (p) => - (p === post || directReplyIds.includes(p.id)) && - p.setDeletedState(user) - ); - Post.deleteMany([post.id, ...directReplyIds]) + label: I18n.t("post.controls.delete_replies.just_the_post"), + action: () => { + post + .destroy(user, opts) .then(refresh) - .catch(popupAjaxError); + .catch((error) => { + popupAjaxError(error); + post.undoDeleteState(); + }); }, }); - bootbox.dialog( - I18n.t("post.controls.delete_replies.confirm"), - buttons - ); + buttons.push({ + label: I18n.t("cancel"), + class: "btn-flat", + }); + + this.dialog.alert({ + title: I18n.t("post.controls.delete_replies.confirm"), + buttons, + }); }); } else { return post @@ -707,34 +707,24 @@ export default Controller.extend(bufferedProperty("model"), { }, deletePostWithConfirmation(post, opts) { - bootbox.confirm( - I18n.t("post.confirm_delete"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - this.send("deletePost", post, opts); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("post.confirm_delete"), + didConfirm: () => this.send("deletePost", post, opts), + }); }, permanentlyDeletePost(post) { - return bootbox.confirm( - I18n.t("post.controls.permanently_delete_confirmation"), - I18n.t("no_value"), - I18n.t("yes_value"), - (result) => { - if (result) { - this.send("deletePost", post, { force_destroy: true }); - } - } - ); + return this.dialog.yesNoConfirm({ + message: I18n.t("post.controls.permanently_delete_confirmation"), + didConfirm: () => { + this.send("deletePost", post, { force_destroy: true }); + }, + }); }, editPost(post) { if (!this.currentUser) { - return bootbox.alert(I18n.t("post.controls.edit_anonymous")); + return this.dialog.alert(I18n.t("post.controls.edit_anonymous")); } else if (!post.can_edit) { return false; } @@ -773,7 +763,7 @@ export default Controller.extend(bufferedProperty("model"), { toggleBookmark(post) { if (!this.currentUser) { - return bootbox.alert(I18n.t("bookmarks.not_bookmarked")); + return this.dialog.alert(I18n.t("bookmarks.not_bookmarked")); } else if (post) { const bookmarkForPost = this.model.bookmarks.find( (bookmark) => @@ -912,38 +902,35 @@ export default Controller.extend(bufferedProperty("model"), { deleteSelected() { const user = this.currentUser; - - bootbox.confirm( - I18n.t("post.delete.confirm", { + this.dialog.yesNoConfirm({ + message: I18n.t("post.delete.confirm", { count: this.selectedPostsCount, }), - (result) => { - if (result) { - // If all posts are selected, it's the same thing as deleting the topic - if (this.selectedAllPosts) { - return this.deleteTopic(); - } - - Post.deleteMany(this.selectedPostIds); - this.get("model.postStream.posts").forEach( - (p) => this.postSelected(p) && p.setDeletedState(user) - ); - this.send("toggleMultiSelect"); + didConfirm: () => { + // If all posts are selected, it's the same thing as deleting the topic + if (this.selectedAllPosts) { + return this.deleteTopic(); } - } - ); + + Post.deleteMany(this.selectedPostIds); + this.get("model.postStream.posts").forEach( + (p) => this.postSelected(p) && p.setDeletedState(user) + ); + this.send("toggleMultiSelect"); + }, + }); }, mergePosts() { - bootbox.confirm( - I18n.t("post.merge.confirm", { count: this.selectedPostsCount }), - (result) => { - if (result) { - Post.mergePosts(this.selectedPostIds); - this.send("toggleMultiSelect"); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("post.merge.confirm", { + count: this.selectedPostsCount, + }), + didConfirm: () => { + Post.mergePosts(this.selectedPostIds); + this.send("toggleMultiSelect"); + }, + }); }, changePostOwner(post) { @@ -1348,25 +1335,22 @@ export default Controller.extend(bufferedProperty("model"), { _maybeClearAllBookmarks() { return new Promise((resolve) => { - bootbox.confirm( - I18n.t("bookmarks.confirm_clear"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - return this.model - .deleteBookmarks() - .then(() => resolve(this.model.clearBookmarks())) - .catch(popupAjaxError) - .finally(() => { - this.model.set("bookmarking", false); - }); - } else { - this.model.set("bookmarking", false); - resolve(); - } - } - ); + this.dialog.yesNoConfirm({ + message: I18n.t("bookmarks.confirm_clear"), + didConfirm: () => { + return this.model + .deleteBookmarks() + .then(() => resolve(this.model.clearBookmarks())) + .catch(popupAjaxError) + .finally(() => { + this.model.set("bookmarking", false); + }); + }, + didCancel: () => { + this.model.set("bookmarking", false); + resolve(); + }, + }); }); }, diff --git a/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js b/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js index b2164921f86..e46c80057c4 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js @@ -303,8 +303,8 @@ acceptance("Bookmarking", function (needs) { }); test("The topic level bookmark button deletes all bookmarks if several posts on the topic are bookmarked", async function (assert) { - const yesButton = "a.btn-primary"; - const noButton = "a.btn-default"; + const yesButton = ".dialog-footer .btn-primary"; + const noButton = ".dialog-footer .btn-default"; await visit("/t/internationalization-localization/280"); await openBookmarkModal(1); @@ -336,6 +336,7 @@ acceptance("Bookmarking", function (needs) { // open the modal and accept deleting await click("#topic-footer-button-bookmark"); + // pauseTest(); await click(yesButton); assert.ok( @@ -412,7 +413,7 @@ acceptance("Bookmarking", function (needs) { "the footer button says Clear Bookmarks because there is more than one" ); await click("#topic-footer-button-bookmark"); - await click("a.btn-primary"); + await click(".dialog-footer .btn-primary"); assert.ok( !exists(".topic-post:first-child button.bookmark.bookmarked"), diff --git a/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js b/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js index eb703cd6da9..5042f2c6141 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/category-edit-test.js @@ -164,14 +164,13 @@ acceptance("Category Edit", function (needs) { await fillIn(".email-in", "duplicate@example.com"); await click("#save-category"); - assert.ok(visible(".bootbox")); assert.strictEqual( - query(".bootbox .modal-body").innerHTML, + query(".dialog-body").textContent.trim(), "duplicate email" ); - await click(".bootbox .btn-primary"); - assert.ok(!visible(".bootbox")); + await click(".dialog-footer .btn-primary"); + assert.ok(!visible(".dialog-body")); }); test("Subcategory list settings", async function (assert) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/group-test.js b/app/assets/javascripts/discourse/tests/acceptance/group-test.js index 194fdab4b20..ad743a727aa 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/group-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/group-test.js @@ -275,14 +275,14 @@ acceptance("Group - Authenticated", function (needs) { await click(".group-details-button button.btn-danger"); assert.strictEqual( - query(".bootbox .modal-body").innerHTML, + query(".dialog-body").textContent.trim(), I18n.t("admin.groups.delete_with_messages_confirm", { count: 2, }), "it should warn about orphan messages" ); - await click(".modal-footer .btn-default"); + await click(".dialog-footer .btn-default"); }); test("Moderator Viewing Group", async function (assert) { diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js index 2ea3fb5b54c..df16cdf32da 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/user-drafts-stream-test.js @@ -18,9 +18,9 @@ acceptance("User Drafts", function (needs) { assert.strictEqual(count(".user-stream-item"), 3, "has drafts"); await click(".user-stream-item:first-child .remove-draft"); - assert.ok(visible(".bootbox")); + assert.ok(visible(".dialog-body")); - await click(".bootbox .btn-primary"); + await click(".dialog-footer .btn-primary"); assert.strictEqual( count(".user-stream-item"), 2, diff --git a/app/assets/javascripts/wizard/addon/components/wizard-step.js b/app/assets/javascripts/wizard/addon/components/wizard-step.js index 8c72f79e15c..4e77594618c 100644 --- a/app/assets/javascripts/wizard/addon/components/wizard-step.js +++ b/app/assets/javascripts/wizard/addon/components/wizard-step.js @@ -10,6 +10,7 @@ const alreadyWarned = {}; export default Component.extend({ router: service(), + dialog: service(), classNameBindings: [":wizard-container__step", "stepClass"], saving: null, @@ -178,16 +179,10 @@ export default Component.extend({ if (unwarned.length) { unwarned.forEach((w) => (alreadyWarned[w] = true)); - return window.bootbox.confirm( - unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - this.advance(); - } - } - ); + return this.dialog.confirm({ + message: unwarned.map((w) => I18n.t(`wizard.${w}`)).join("\n"), + didConfirm: () => this.advance(), + }); } } diff --git a/app/assets/stylesheets/common/base/dialog.scss b/app/assets/stylesheets/common/base/dialog.scss index d5e60bf7c2e..64067180b89 100644 --- a/app/assets/stylesheets/common/base/dialog.scss +++ b/app/assets/stylesheets/common/base/dialog.scss @@ -25,6 +25,7 @@ opacity: 0; } } + .dialog-overlay { background: rgba(var(--always-black-rgb), 0.65); animation: fade-in 250ms both; @@ -43,6 +44,7 @@ .dialog-body { overflow-y: auto; max-height: 400px; + max-width: 800px; padding: 1em; } @@ -60,6 +62,7 @@ .dialog-close { margin-left: auto; flex-basis: content; + padding-left: 15px; .d-icon { color: var(--primary-high); } diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 35103561416..f88e9f37086 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3289,12 +3289,12 @@ en: delete: confirm: one: "Are you sure you want to delete that post?" - other: "Are you sure you want to delete those %{count} posts?" + other: "Are you sure you want to delete these %{count} posts?" merge: confirm: - one: "Are you sure you want to merge those posts?" - other: "Are you sure you want to merge those %{count} posts?" + one: "Are you sure you want to merge these posts?" + other: "Are you sure you want to merge these %{count} posts?" revisions: controls: diff --git a/plugins/poll/assets/javascripts/widgets/discourse-poll.js b/plugins/poll/assets/javascripts/widgets/discourse-poll.js index 04565e05273..a2972b84711 100644 --- a/plugins/poll/assets/javascripts/widgets/discourse-poll.js +++ b/plugins/poll/assets/javascripts/widgets/discourse-poll.js @@ -13,7 +13,6 @@ import { popupAjaxError } from "discourse/lib/ajax-error"; import { relativeAge } from "discourse/lib/formatter"; import round from "discourse/lib/round"; import showModal from "discourse/lib/show-modal"; -import bootbox from "bootbox"; import { applyLocalDates } from "discourse/lib/local-dates"; const FETCH_VOTERS_COUNT = 25; @@ -247,6 +246,7 @@ createWidget("discourse-poll-number-results", { createWidget("discourse-poll-container", { tagName: "div.poll-container", buildKey: (attrs) => `poll-container-${attrs.id}`, + services: ["dialog"], defaultState() { return { voters: [] }; @@ -373,7 +373,7 @@ createWidget("discourse-poll-container", { if (error) { popupAjaxError(error); } else { - bootbox.alert(I18n.t("poll.error_while_fetching_voters")); + this.dialog.alert(I18n.t("poll.error_while_fetching_voters")); } }); }, @@ -763,6 +763,7 @@ createWidget("discourse-poll-buttons", { export default createWidget("discourse-poll", { tagName: "div", buildKey: (attrs) => `poll-${attrs.id}`, + services: ["dialog"], buildAttributes(attrs) { let cssClasses = "poll"; @@ -875,43 +876,40 @@ export default createWidget("discourse-poll", { return; } - bootbox.confirm( - I18n.t(this.isClosed() ? "poll.open.confirm" : "poll.close.confirm"), - I18n.t("no_value"), - I18n.t("yes_value"), - (confirmed) => { - if (confirmed) { - state.loading = true; - const status = this.isClosed() ? "open" : "closed"; - - ajax("/polls/toggle_status", { - type: "PUT", - data: { - post_id: post.id, - poll_name: poll.name, - status, - }, + this.dialog.yesNoConfirm({ + message: I18n.t( + this.isClosed() ? "poll.open.confirm" : "poll.close.confirm" + ), + didConfirm: () => { + state.loading = true; + const status = this.isClosed() ? "open" : "closed"; + ajax("/polls/toggle_status", { + type: "PUT", + data: { + post_id: post.id, + poll_name: poll.name, + status, + }, + }) + .then(() => { + poll.set("status", status); + if (poll.results === "on_close") { + state.showResults = status === "closed"; + } + this.scheduleRerender(); }) - .then(() => { - poll.set("status", status); - if (poll.results === "on_close") { - state.showResults = status === "closed"; - } - this.scheduleRerender(); - }) - .catch((error) => { - if (error) { - popupAjaxError(error); - } else { - bootbox.alert(I18n.t("poll.error_while_toggling_status")); - } - }) - .finally(() => { - state.loading = false; - }); - } - } - ); + .catch((error) => { + if (error) { + popupAjaxError(error); + } else { + this.dialog.alert(I18n.t("poll.error_while_toggling_status")); + } + }) + .finally(() => { + state.loading = false; + }); + }, + }); }, toggleResults() { @@ -976,7 +974,7 @@ export default createWidget("discourse-poll", { if (error) { popupAjaxError(error); } else { - bootbox.alert(I18n.t("poll.error_while_exporting_results")); + this.dialog.alert(I18n.t("poll.error_while_exporting_results")); } }); }, @@ -1063,7 +1061,7 @@ export default createWidget("discourse-poll", { if (error) { popupAjaxError(error); } else { - bootbox.alert(I18n.t("poll.error_while_casting_votes")); + this.dialog.alert(I18n.t("poll.error_while_casting_votes")); } }) .finally(() => {