diff --git a/app/assets/javascripts/admin/addon/components/embeddable-host.js b/app/assets/javascripts/admin/addon/components/embeddable-host.js
index 328ad06e1e..320b173bfb 100644
--- a/app/assets/javascripts/admin/addon/components/embeddable-host.js
+++ b/app/assets/javascripts/admin/addon/components/embeddable-host.js
@@ -1,9 +1,9 @@
import Category from "discourse/models/category";
import Component from "@ember/component";
import I18n from "I18n";
-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";
import { isEmpty } from "@ember/utils";
import { or } from "@ember/object/computed";
import { popupAjaxError } from "discourse/lib/ajax-error";
@@ -13,6 +13,7 @@ export default Component.extend(bufferedProperty("host"), {
tagName: "tr",
categoryId: null,
category: null,
+ dialog: service(),
editing: or("host.isNew", "editToggled"),
@@ -61,12 +62,13 @@ export default Component.extend(bufferedProperty("host"), {
},
delete() {
- bootbox.confirm(I18n.t("admin.embedding.confirm_delete"), (result) => {
- if (result) {
- this.host.destroyRecord().then(() => {
+ return this.dialog.confirm({
+ message: I18n.t("admin.embedding.confirm_delete"),
+ didConfirm: () => {
+ return this.host.destroyRecord().then(() => {
this.deleteHost(this.host);
});
- }
+ },
});
},
diff --git a/app/assets/javascripts/admin/addon/controllers/admin-customize-email-templates-edit.js b/app/assets/javascripts/admin/addon/controllers/admin-customize-email-templates-edit.js
index d20f812ca3..c784deb0ec 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-customize-email-templates-edit.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-customize-email-templates-edit.js
@@ -1,13 +1,14 @@
import Controller, { inject as controller } from "@ember/controller";
import I18n from "I18n";
import { action } from "@ember/object";
-import bootbox from "bootbox";
import { bufferedProperty } from "discourse/mixins/buffered-content";
import discourseComputed from "discourse-common/utils/decorators";
import { popupAjaxError } from "discourse/lib/ajax-error";
+import { inject as service } from "@ember/service";
export default Controller.extend(bufferedProperty("emailTemplate"), {
adminCustomizeEmailTemplates: controller(),
+ dialog: service(),
emailTemplate: null,
saved: false,
@@ -42,20 +43,19 @@ export default Controller.extend(bufferedProperty("emailTemplate"), {
@action
revertChanges() {
this.set("saved", false);
- bootbox.confirm(
- I18n.t("admin.customize.email_templates.revert_confirm"),
- (result) => {
- if (result) {
- this.emailTemplate
- .revert()
- .then((props) => {
- const buffered = this.buffered;
- buffered.setProperties(props);
- this.commitBuffer();
- })
- .catch(popupAjaxError);
- }
- }
- );
+
+ this.dialog.yesNoConfirm({
+ title: I18n.t("admin.customize.email_templates.revert_confirm"),
+ didConfirm: () => {
+ return this.emailTemplate
+ .revert()
+ .then((props) => {
+ const buffered = this.buffered;
+ buffered.setProperties(props);
+ this.commitBuffer();
+ })
+ .catch(popupAjaxError);
+ },
+ });
},
});
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 61ffda925e..45d1973305 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
@@ -10,7 +10,6 @@ import Controller from "@ember/controller";
import EmberObject from "@ember/object";
import I18n from "I18n";
import ThemeSettings from "admin/models/theme-settings";
-import bootbox from "bootbox";
import discourseComputed from "discourse-common/utils/decorators";
import { makeArray } from "discourse-common/lib/helpers";
import { popupAjaxError } from "discourse/lib/ajax-error";
@@ -306,14 +305,10 @@ export default Controller.extend({
editTheme() {
if (this.get("model.remote_theme.is_git")) {
- bootbox.confirm(
- I18n.t("admin.customize.theme.edit_confirm"),
- (result) => {
- if (result) {
- this.transitionToEditRoute();
- }
- }
- );
+ this.dialog.confirm({
+ message: I18n.t("admin.customize.theme.edit_confirm"),
+ didConfirm: () => this.transitionToEditRoute(),
+ });
} else {
this.transitionToEditRoute();
}
diff --git a/app/assets/javascripts/admin/addon/controllers/admin-user-fields.js b/app/assets/javascripts/admin/addon/controllers/admin-user-fields.js
index bafcc02937..619c1cf629 100644
--- a/app/assets/javascripts/admin/addon/controllers/admin-user-fields.js
+++ b/app/assets/javascripts/admin/addon/controllers/admin-user-fields.js
@@ -1,12 +1,13 @@
import { gte, sort } from "@ember/object/computed";
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";
const MAX_FIELDS = 30;
export default Controller.extend({
+ dialog: service(),
fieldTypes: null,
createDisabled: gte("model.length", MAX_FIELDS),
sortedFields: sort("model", "fieldSortOrder"),
@@ -53,18 +54,17 @@ export default Controller.extend({
// Only confirm if we already been saved
if (f.get("id")) {
- bootbox.confirm(
- I18n.t("admin.user_fields.delete_confirm"),
- function (result) {
- if (result) {
- f.destroyRecord()
- .then(function () {
- model.removeObject(f);
- })
- .catch(popupAjaxError);
- }
- }
- );
+ this.dialog.yesNoConfirm({
+ message: I18n.t("admin.user_fields.delete_confirm"),
+ didConfirm: () => {
+ return f
+ .destroyRecord()
+ .then(function () {
+ model.removeObject(f);
+ })
+ .catch(popupAjaxError);
+ },
+ });
} else {
model.removeObject(f);
}
diff --git a/app/assets/javascripts/admin/addon/mixins/penalty-controller.js b/app/assets/javascripts/admin/addon/mixins/penalty-controller.js
index 55514dfe48..eb5d56e8cf 100644
--- a/app/assets/javascripts/admin/addon/mixins/penalty-controller.js
+++ b/app/assets/javascripts/admin/addon/mixins/penalty-controller.js
@@ -2,11 +2,12 @@ import I18n from "I18n";
import Mixin from "@ember/object/mixin";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { Promise } from "rsvp";
-import bootbox from "bootbox";
import { extractError } from "discourse/lib/ajax-error";
import { next } from "@ember/runloop";
+import { inject as service } from "@ember/service";
export default Mixin.create(ModalFunctionality, {
+ dialog: service(),
errorMessage: null,
reason: null,
message: null,
@@ -40,15 +41,15 @@ export default Mixin.create(ModalFunctionality, {
(this.message && this.message.length > 1))
) {
this.send("hideModal");
- bootbox.confirm(I18n.t("admin.user.confirm_cancel_penalty"), (result) => {
- if (result) {
+ this.dialog.confirm({
+ message: I18n.t("admin.user.confirm_cancel_penalty"),
+ didConfirm: () => {
next(() => {
this.set("confirmClose", true);
this.send("closeModal");
});
- } else {
- next(() => this.send("reopenModal"));
- }
+ },
+ didCancel: () => this.send("reopenModal"),
});
return false;
}
diff --git a/app/assets/javascripts/admin/addon/routes/admin-customize-email-style-edit.js b/app/assets/javascripts/admin/addon/routes/admin-customize-email-style-edit.js
index 4453dbe672..847b21a75c 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-customize-email-style-edit.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-customize-email-style-edit.js
@@ -1,8 +1,9 @@
import I18n from "I18n";
import Route from "@ember/routing/route";
-import bootbox from "bootbox";
+import { inject as service } from "@ember/service";
export default Route.extend({
+ dialog: service(),
model(params) {
return {
model: this.modelFor("adminCustomizeEmailStyle"),
@@ -26,17 +27,15 @@ export default Route.extend({
transition.intent.name !== this.routeName
) {
transition.abort();
- bootbox.confirm(
- I18n.t("admin.customize.theme.unsaved_changes_alert"),
- I18n.t("admin.customize.theme.discard"),
- I18n.t("admin.customize.theme.stay"),
- (result) => {
- if (!result) {
- this._shouldAlertUnsavedChanges = false;
- transition.retry();
- }
- }
- );
+ this.dialog.confirm({
+ message: I18n.t("admin.customize.theme.unsaved_changes_alert"),
+ confirmButtonLabel: "admin.customize.theme.discard",
+ cancelButtonLabel: "admin.customize.theme.stay",
+ didConfirm: () => {
+ this._shouldAlertUnsavedChanges = false;
+ transition.retry();
+ },
+ });
}
},
},
diff --git a/app/assets/javascripts/admin/addon/routes/admin-customize-themes-edit.js b/app/assets/javascripts/admin/addon/routes/admin-customize-themes-edit.js
index 0d55baa6ce..c72e9d0731 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-customize-themes-edit.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-customize-themes-edit.js
@@ -1,8 +1,10 @@
import I18n from "I18n";
import Route from "@ember/routing/route";
-import bootbox from "bootbox";
+import { inject as service } from "@ember/service";
export default Route.extend({
+ dialog: service(),
+
model(params) {
const all = this.modelFor("adminCustomizeThemes");
const model = all.findBy("id", parseInt(params.theme_id, 10));
@@ -56,17 +58,16 @@ export default Route.extend({
transition.intent.name !== this.routeName
) {
transition.abort();
- bootbox.confirm(
- I18n.t("admin.customize.theme.unsaved_changes_alert"),
- I18n.t("admin.customize.theme.discard"),
- I18n.t("admin.customize.theme.stay"),
- (result) => {
- if (!result) {
- this.set("shouldAlertUnsavedChanges", false);
- transition.retry();
- }
- }
- );
+
+ this.dialog.confirm({
+ message: I18n.t("admin.customize.theme.unsaved_changes_alert"),
+ confirmButtonLabel: "admin.customize.theme.discard",
+ cancelButtonLabel: "admin.customize.theme.stay",
+ didConfirm: () => {
+ this.set("shouldAlertUnsavedChanges", false);
+ transition.retry();
+ },
+ });
}
},
},
diff --git a/app/assets/javascripts/admin/addon/routes/admin-customize-themes-show.js b/app/assets/javascripts/admin/addon/routes/admin-customize-themes-show.js
index 6c23a36109..156dbf9dd0 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-customize-themes-show.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-customize-themes-show.js
@@ -2,23 +2,11 @@ import { COMPONENTS, THEMES } from "admin/models/theme";
import I18n from "I18n";
import Route from "@ember/routing/route";
import { scrollTop } from "discourse/mixins/scroll-top";
-import bootbox from "bootbox";
-
-export function showUnassignedComponentWarning(theme, callback) {
- bootbox.confirm(
- I18n.t("admin.customize.theme.unsaved_parent_themes"),
- I18n.t("admin.customize.theme.discard"),
- I18n.t("admin.customize.theme.stay"),
- (result) => {
- if (!result) {
- theme.set("recentlyInstalled", false);
- }
- callback(result);
- }
- );
-}
+import { inject as service } from "@ember/service";
export default Route.extend({
+ dialog: service(),
+
serialize(model) {
return { theme_id: model.get("id") };
},
@@ -72,10 +60,14 @@ export default Route.extend({
const model = this.controller.model;
if (model.warnUnassignedComponent) {
transition.abort();
- showUnassignedComponentWarning(model, (result) => {
- if (!result) {
+
+ this.dialog.yesNoConfirm({
+ message: I18n.t("admin.customize.theme.unsaved_parent_themes"),
+ didConfirm: () => {
+ model.set("recentlyInstalled", false);
transition.retry();
- }
+ },
+ didCancel: () => model.set("recentlyInstalled", false),
});
}
},
diff --git a/app/assets/javascripts/admin/addon/routes/admin-customize-themes.js b/app/assets/javascripts/admin/addon/routes/admin-customize-themes.js
index 8734675eff..22878ded22 100644
--- a/app/assets/javascripts/admin/addon/routes/admin-customize-themes.js
+++ b/app/assets/javascripts/admin/addon/routes/admin-customize-themes.js
@@ -1,9 +1,12 @@
import Route from "@ember/routing/route";
import showModal from "discourse/lib/show-modal";
+import I18n from "I18n";
import { next } from "@ember/runloop";
-import { showUnassignedComponentWarning } from "admin/routes/admin-customize-themes-show";
+import { inject as service } from "@ember/service";
export default Route.extend({
+ dialog: service(),
+
queryParams: {
repoUrl: null,
repoName: null,
@@ -35,11 +38,13 @@ export default Route.extend({
const currentTheme = this.controllerFor(
"adminCustomizeThemes.show"
).model;
- if (currentTheme && currentTheme.warnUnassignedComponent) {
- showUnassignedComponentWarning(currentTheme, (result) => {
- if (!result) {
+ if (currentTheme?.warnUnassignedComponent) {
+ this.dialog.yesNoConfirm({
+ message: I18n.t("admin.customize.theme.unsaved_parent_themes"),
+ didConfirm: () => {
+ currentTheme.set("recentlyInstalled", false);
showModal("admin-install-theme", { admin: true });
- }
+ },
});
} else {
showModal("admin-install-theme", { admin: true });
diff --git a/app/assets/javascripts/admin/addon/services/admin-tools.js b/app/assets/javascripts/admin/addon/services/admin-tools.js
index 3d06845508..5fce78cb33 100644
--- a/app/assets/javascripts/admin/addon/services/admin-tools.js
+++ b/app/assets/javascripts/admin/addon/services/admin-tools.js
@@ -3,10 +3,9 @@ import I18n from "I18n";
import { Promise } from "rsvp";
import Service, { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
-import bootbox from "bootbox";
import { getOwner } from "discourse-common/lib/get-owner";
-import { iconHTML } from "discourse-common/lib/icon-library";
import showModal from "discourse/lib/show-modal";
+import { htmlSafe } from "@ember/template";
// A service that can act as a bridge between the front end Discourse application
// and the admin application. Use this if you need front end code to access admin
@@ -79,58 +78,50 @@ export default Service.extend({
: Promise.resolve();
return tryEmail.then(() => {
- let message = I18n.messageFormat("flagging.delete_confirm_MF", {
- POSTS: adminUser.get("post_count"),
- TOPICS: adminUser.get("topic_count"),
- email:
- adminUser.get("email") || I18n.t("flagging.hidden_email_address"),
- ip_address:
- adminUser.get("ip_address") || I18n.t("flagging.ip_address_missing"),
- });
+ let message = htmlSafe(
+ I18n.messageFormat("flagging.delete_confirm_MF", {
+ POSTS: adminUser.get("post_count"),
+ TOPICS: adminUser.get("topic_count"),
+ email:
+ adminUser.get("email") || I18n.t("flagging.hidden_email_address"),
+ ip_address:
+ adminUser.get("ip_address") ||
+ I18n.t("flagging.ip_address_missing"),
+ })
+ );
let userId = adminUser.get("id");
return new Promise((resolve, reject) => {
- const buttons = [
- {
- label: I18n.t("composer.cancel"),
- class: "d-modal-cancel",
- link: true,
- },
- {
- label:
- `${iconHTML("exclamation-triangle")} ` +
- I18n.t("flagging.yes_delete_spammer"),
- class: "btn btn-danger confirm-delete",
- callback() {
- return ajax(`/admin/users/${userId}.json`, {
- type: "DELETE",
- data: {
- delete_posts: true,
- block_email: true,
- block_urls: true,
- block_ip: true,
- delete_as_spammer: true,
- context: window.location.pathname,
- },
+ this.dialog.deleteConfirm({
+ message,
+ class: "flagging-delete-spammer",
+ confirmButtonLabel: "flagging.yes_delete_spammer",
+ confirmButtonIcon: "exclamation-triangle",
+ didConfirm: () => {
+ return ajax(`/admin/users/${userId}.json`, {
+ type: "DELETE",
+ data: {
+ delete_posts: true,
+ block_email: true,
+ block_urls: true,
+ block_ip: true,
+ delete_as_spammer: true,
+ context: window.location.pathname,
+ },
+ })
+ .then((result) => {
+ if (result.deleted) {
+ resolve();
+ } else {
+ throw new Error("failed to delete");
+ }
})
- .then((result) => {
- if (result.deleted) {
- resolve();
- } else {
- throw new Error("failed to delete");
- }
- })
- .catch(() => {
- this.dialog.alert(I18n.t("admin.user.delete_failed"));
- reject();
- });
- },
+ .catch(() => {
+ this.dialog.alert(I18n.t("admin.user.delete_failed"));
+ reject();
+ });
},
- ];
-
- bootbox.dialog(message, buttons, {
- classes: "flagging-delete-spammer",
});
});
});
diff --git a/app/assets/javascripts/discourse/app/components/bookmark-list.js b/app/assets/javascripts/discourse/app/components/bookmark-list.js
index 594bf4ff11..6ea4c9cb63 100644
--- a/app/assets/javascripts/discourse/app/components/bookmark-list.js
+++ b/app/assets/javascripts/discourse/app/components/bookmark-list.js
@@ -1,7 +1,6 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { next, schedule } from "@ember/runloop";
-import bootbox from "bootbox";
import { openBookmarkModal } from "discourse/controllers/bookmark";
import { ajax } from "discourse/lib/ajax";
import {
@@ -11,8 +10,10 @@ import {
import Scrolling from "discourse/mixins/scrolling";
import I18n from "I18n";
import { Promise } from "rsvp";
+import { inject as service } from "@ember/service";
export default Component.extend(Scrolling, {
+ dialog: service(),
classNames: ["bookmark-list-wrapper"],
didInsertElement() {
@@ -64,12 +65,10 @@ export default Component.extend(Scrolling, {
if (!bookmark.reminder_at) {
return deleteBookmark();
}
- bootbox.confirm(I18n.t("bookmarks.confirm_delete"), (result) => {
- if (result) {
- deleteBookmark();
- } else {
- resolve(false);
- }
+ this.dialog.deleteConfirm({
+ message: I18n.t("bookmarks.confirm_delete"),
+ didConfirm: () => deleteBookmark(),
+ didCancel: () => resolve(false),
});
});
},
diff --git a/app/assets/javascripts/discourse/app/components/bookmark.js b/app/assets/javascripts/discourse/app/components/bookmark.js
index 14997c5aad..879ef027fc 100644
--- a/app/assets/javascripts/discourse/app/components/bookmark.js
+++ b/app/assets/javascripts/discourse/app/components/bookmark.js
@@ -11,7 +11,6 @@ import {
} from "discourse/lib/time-shortcut";
import { action } from "@ember/object";
import { ajax } from "discourse/lib/ajax";
-import bootbox from "bootbox";
import discourseComputed, { bind } from "discourse-common/utils/decorators";
import { formattedReminderTime } from "discourse/lib/bookmark";
import { and, notEmpty } from "@ember/object/computed";
@@ -377,10 +376,9 @@ export default Component.extend({
};
if (this.existingBookmarkHasReminder) {
- bootbox.confirm(I18n.t("bookmarks.confirm_delete"), (result) => {
- if (result) {
- deleteAction();
- }
+ this.dialog.deleteConfirm({
+ message: I18n.t("bookmarks.confirm_delete"),
+ didConfirm: () => deleteAction(),
});
} else {
deleteAction();
diff --git a/app/assets/javascripts/discourse/app/components/reviewable-item.js b/app/assets/javascripts/discourse/app/components/reviewable-item.js
index 73c90d665a..2ce56fd43b 100644
--- a/app/assets/javascripts/discourse/app/components/reviewable-item.js
+++ b/app/assets/javascripts/discourse/app/components/reviewable-item.js
@@ -2,13 +2,13 @@ import Category from "discourse/models/category";
import Component from "@ember/component";
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
-import bootbox from "bootbox";
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 { action, set } from "@ember/object";
import showModal from "discourse/lib/show-modal";
+import { inject as service } from "@ember/service";
let _components = {};
@@ -22,6 +22,7 @@ export function addPluginReviewableParam(reviewableType, param) {
export default Component.extend({
adminTools: optionalService(),
+ dialog: service(),
tagName: "",
updating: null,
editing: false,
@@ -269,14 +270,13 @@ export default Component.extend({
return;
}
- let msg = performableAction.get("confirm_message");
+ const message = 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(performableAction);
- }
+ if (message) {
+ this.dialog.confirm({
+ message,
+ didConfirm: () => this._performConfirmed(performableAction),
});
} else if (requireRejectReason) {
showModal("reject-reason-reviewable", {
diff --git a/app/assets/javascripts/discourse/app/components/shared-draft-controls.js b/app/assets/javascripts/discourse/app/components/shared-draft-controls.js
index a68fe3c1eb..465edcbb6d 100644
--- a/app/assets/javascripts/discourse/app/components/shared-draft-controls.js
+++ b/app/assets/javascripts/discourse/app/components/shared-draft-controls.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 { inject as service } from "@ember/service";
export default Component.extend({
tagName: "",
+ dialog: service(),
publishing: false,
@discourseComputed("topic.destination_category_id")
@@ -18,11 +19,12 @@ export default Component.extend({
},
publish() {
- bootbox.confirm(I18n.t("shared_drafts.confirm_publish"), (result) => {
- if (result) {
+ this.dialog.yesNoConfirm({
+ message: I18n.t("shared_drafts.confirm_publish"),
+ didConfirm: () => {
this.set("publishing", true);
const destinationCategoryId = this.topic.destination_category_id;
- this.topic
+ return this.topic
.publish()
.then(() => {
this.topic.setProperties({
@@ -34,7 +36,7 @@ export default Component.extend({
.finally(() => {
this.set("publishing", false);
});
- }
+ },
});
},
},
diff --git a/app/assets/javascripts/discourse/app/components/tag-info.js b/app/assets/javascripts/discourse/app/components/tag-info.js
index 21da9a92f1..201dec9ba6 100644
--- a/app/assets/javascripts/discourse/app/components/tag-info.js
+++ b/app/assets/javascripts/discourse/app/components/tag-info.js
@@ -2,12 +2,12 @@ import { and, reads } from "@ember/object/computed";
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 { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service";
import { action } from "@ember/object";
+import { htmlSafe } from "@ember/template";
export default Component.extend({
dialog: service(),
@@ -100,19 +100,18 @@ export default Component.extend({
@action
deleteSynonym(tag, event) {
event?.preventDefault();
- bootbox.confirm(
- I18n.t("tagging.delete_synonym_confirm", { tag_name: tag.text }),
- (result) => {
- if (!result) {
- return;
- }
- tag
+ this.dialog.yesNoConfirm({
+ message: I18n.t("tagging.delete_synonym_confirm", {
+ tag_name: tag.text,
+ }),
+ didConfirm: () => {
+ return tag
.destroyRecord()
.then(() => this.tagInfo.synonyms.removeObject(tag))
.catch(popupAjaxError);
- }
- );
+ },
+ });
},
actions: {
@@ -146,17 +145,15 @@ export default Component.extend({
},
addSynonyms() {
- bootbox.confirm(
- I18n.t("tagging.add_synonyms_explanation", {
- count: this.newSynonyms.length,
- tag_name: this.tagInfo.name,
- }),
- (result) => {
- if (!result) {
- return;
- }
-
- ajax(`/tag/${this.tagInfo.name}/synonyms`, {
+ this.dialog.confirm({
+ message: htmlSafe(
+ I18n.t("tagging.add_synonyms_explanation", {
+ count: this.newSynonyms.length,
+ tag_name: this.tagInfo.name,
+ })
+ ),
+ didConfirm: () => {
+ return ajax(`/tag/${this.tagInfo.name}/synonyms`, {
type: "POST",
data: {
synonyms: this.newSynonyms,
@@ -177,8 +174,8 @@ export default Component.extend({
}
})
.catch(popupAjaxError);
- }
- );
+ },
+ });
},
},
});
diff --git a/app/assets/javascripts/discourse/app/controllers/preferences/profile.js b/app/assets/javascripts/discourse/app/controllers/preferences/profile.js
index a3ea290db2..e396e3e4df 100644
--- a/app/assets/javascripts/discourse/app/controllers/preferences/profile.js
+++ b/app/assets/javascripts/discourse/app/controllers/preferences/profile.js
@@ -2,15 +2,16 @@ import Controller from "@ember/controller";
import EmberObject from "@ember/object";
import I18n from "I18n";
import { ajax } from "discourse/lib/ajax";
-import bootbox from "bootbox";
import { cookAsync } from "discourse/lib/text";
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { readOnly } from "@ember/object/computed";
import showModal from "discourse/lib/show-modal";
+import { inject as service } from "@ember/service";
export default Controller.extend({
+ dialog: service(),
init() {
this._super(...arguments);
this.saveAttrNames = [
@@ -79,20 +80,18 @@ export default Controller.extend({
},
clearFeaturedTopicFromProfile() {
- bootbox.confirm(
- I18n.t("user.feature_topic_on_profile.clear.warning"),
- (result) => {
- if (result) {
- ajax(`/u/${this.model.username}/clear-featured-topic`, {
- type: "PUT",
+ this.dialog.yesNoConfirm({
+ message: I18n.t("user.feature_topic_on_profile.clear.warning"),
+ didConfirm: () => {
+ return ajax(`/u/${this.model.username}/clear-featured-topic`, {
+ type: "PUT",
+ })
+ .then(() => {
+ this.model.set("featured_topic", null);
})
- .then(() => {
- this.model.set("featured_topic", null);
- })
- .catch(popupAjaxError);
- }
- }
- );
+ .catch(popupAjaxError);
+ },
+ });
},
useCurrentTimezone() {
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 fc5293054a..91f77db8a0 100644
--- a/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js
+++ b/app/assets/javascripts/discourse/app/controllers/preferences/second-factor.js
@@ -5,14 +5,14 @@ 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";
import { findAll } from "discourse/models/login-method";
-import { iconHTML } from "discourse-common/lib/icon-library";
import { popupAjaxError } from "discourse/lib/ajax-error";
import showModal from "discourse/lib/show-modal";
+import { inject as service } from "@ember/service";
export default Controller.extend(CanCheckEmails, {
+ dialog: service(),
loading: false,
dirty: false,
resetPasswordLoading: false,
@@ -127,34 +127,21 @@ export default Controller.extend(CanCheckEmails, {
if (this.loading) {
return;
}
- const message = I18n.t("user.second_factor.disable_confirm");
- const buttons = [
- {
- label: I18n.t("cancel"),
- class: "d-modal-cancel",
- link: true,
- },
- {
- icon: iconHTML("ban"),
- label: I18n.t("user.second_factor.disable"),
- class: "btn-danger btn-icon-text",
- callback: () => {
- this.model
- .disableAllSecondFactors()
- .then(() => {
- const usernameLower = this.model.username.toLowerCase();
- DiscourseURL.redirectTo(
- userPath(`${usernameLower}/preferences`)
- );
- })
- .catch((e) => this.handleError(e))
- .finally(() => this.set("loading", false));
- },
- },
- ];
- bootbox.dialog(message, buttons, {
- classes: "disable-second-factor-modal",
+ this.dialog.deleteConfirm({
+ title: I18n.t("user.second_factor.disable_confirm"),
+ confirmButtonLabel: "user.second_factor.disable",
+ confirmButtonIcon: "ban",
+ didConfirm: () => {
+ this.model
+ .disableAllSecondFactors()
+ .then(() => {
+ const usernameLower = this.model.username.toLowerCase();
+ DiscourseURL.redirectTo(userPath(`${usernameLower}/preferences`));
+ })
+ .catch((e) => this.handleError(e))
+ .finally(() => this.set("loading", false));
+ },
});
},
diff --git a/app/assets/javascripts/discourse/app/controllers/tag-show.js b/app/assets/javascripts/discourse/app/controllers/tag-show.js
index 2f9c493516..cc32873bbc 100644
--- a/app/assets/javascripts/discourse/app/controllers/tag-show.js
+++ b/app/assets/javascripts/discourse/app/controllers/tag-show.js
@@ -7,7 +7,6 @@ import I18n from "I18n";
import NavItem from "discourse/models/nav-item";
import Topic from "discourse/models/topic";
import { readOnly } from "@ember/object/computed";
-import bootbox from "bootbox";
import { endWith } from "discourse/lib/computed";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
@@ -167,15 +166,14 @@ export default DiscoverySortableController.extend(
});
}
- bootbox.confirm(confirmText, (result) => {
- if (!result) {
- return;
- }
-
- this.tag
- .destroyRecord()
- .then(() => this.transitionToRoute("tags.index"))
- .catch(() => this.dialog.alert(I18n.t("generic_error")));
+ this.dialog.deleteConfirm({
+ message: confirmText,
+ didConfirm: () => {
+ return this.tag
+ .destroyRecord()
+ .then(() => this.transitionToRoute("tags.index"))
+ .catch(() => this.dialog.alert(I18n.t("generic_error")));
+ },
});
},
diff --git a/app/assets/javascripts/discourse/app/controllers/tags-index.js b/app/assets/javascripts/discourse/app/controllers/tags-index.js
index eff2f1d30b..09d71e89c8 100644
--- a/app/assets/javascripts/discourse/app/controllers/tags-index.js
+++ b/app/assets/javascripts/discourse/app/controllers/tags-index.js
@@ -3,7 +3,6 @@ import { alias, notEmpty } from "@ember/object/computed";
import Controller from "@ember/controller";
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 showModal from "discourse/lib/show-modal";
@@ -91,23 +90,20 @@ export default Controller.extend({
tags: joinedTags,
});
- const string = I18n.t("tagging.delete_unused_confirmation", {
+ const message = I18n.t("tagging.delete_unused_confirmation", {
count: tags.length,
tags: tagsString,
});
- bootbox.confirm(
- string,
- I18n.t("tagging.cancel_delete_unused"),
- I18n.t("tagging.delete_unused"),
- (proceed) => {
- if (proceed) {
- ajax("/tags/unused", { type: "DELETE" })
- .then(() => this.send("triggerRefresh"))
- .catch(popupAjaxError);
- }
- }
- );
+ this.dialog.deleteConfirm({
+ message,
+ confirmButtonLabel: "tagging.delete_unused",
+ didConfirm: () => {
+ return ajax("/tags/unused", { type: "DELETE" })
+ .then(() => this.send("triggerRefresh"))
+ .catch(popupAjaxError);
+ },
+ });
})
.catch(popupAjaxError);
},
diff --git a/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js b/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js
index 7efe21e137..205cc147a4 100644
--- a/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js
+++ b/app/assets/javascripts/discourse/app/controllers/topic-bulk-actions.js
@@ -4,7 +4,6 @@ import I18n from "I18n";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { Promise } from "rsvp";
import Topic from "discourse/models/topic";
-import bootbox from "bootbox";
import { inject as service } from "@ember/service";
@@ -320,16 +319,12 @@ export default Controller.extend(ModalFunctionality, {
},
removeTags() {
- bootbox.confirm(
- I18n.t("topics.bulk.confirm_remove_tags", {
+ this.dialog.deleteConfirm({
+ message: I18n.t("topics.bulk.confirm_remove_tags", {
count: this.get("model.topics").length,
}),
- (result) => {
- if (result) {
- this.performAndRefresh({ type: "remove_tags" });
- }
- }
- );
+ didConfirm: () => this.performAndRefresh({ type: "remove_tags" }),
+ });
},
},
});
diff --git a/app/assets/javascripts/discourse/app/controllers/user-invited-show.js b/app/assets/javascripts/discourse/app/controllers/user-invited-show.js
index 008a4b5946..7ec24665f5 100644
--- a/app/assets/javascripts/discourse/app/controllers/user-invited-show.js
+++ b/app/assets/javascripts/discourse/app/controllers/user-invited-show.js
@@ -1,7 +1,6 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { equal, reads } from "@ember/object/computed";
-import bootbox from "bootbox";
import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseDebounce from "discourse-common/lib/debounce";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
@@ -9,8 +8,10 @@ import { popupAjaxError } from "discourse/lib/ajax-error";
import showModal from "discourse/lib/show-modal";
import Invite from "discourse/models/invite";
import I18n from "I18n";
+import { inject as service } from "@ember/service";
export default Controller.extend({
+ dialog: service(),
user: null,
model: null,
filter: null,
@@ -93,15 +94,16 @@ export default Controller.extend({
@action
destroyAllExpired() {
- bootbox.confirm(I18n.t("user.invited.remove_all_confirm"), (confirm) => {
- if (confirm) {
- Invite.destroyAllExpired()
+ this.dialog.deleteConfirm({
+ message: I18n.t("user.invited.remove_all_confirm"),
+ didConfirm: () => {
+ return Invite.destroyAllExpired()
.then(() => {
this.set("removedAll", true);
this.send("triggerRefresh");
})
.catch(popupAjaxError);
- }
+ },
});
},
@@ -113,12 +115,13 @@ export default Controller.extend({
@action
reinviteAll() {
- bootbox.confirm(I18n.t("user.invited.reinvite_all_confirm"), (confirm) => {
- if (confirm) {
- Invite.reinviteAll()
+ this.dialog.yesNoConfirm({
+ message: I18n.t("user.invited.reinvite_all_confirm"),
+ didConfirm: () => {
+ return Invite.reinviteAll()
.then(() => this.set("reinvitedAll", true))
.catch(popupAjaxError);
- }
+ },
});
},
diff --git a/app/assets/javascripts/discourse/app/widgets/private-message-map.js b/app/assets/javascripts/discourse/app/widgets/private-message-map.js
index 435df6c0de..781d2a8a3c 100644
--- a/app/assets/javascripts/discourse/app/widgets/private-message-map.js
+++ b/app/assets/javascripts/discourse/app/widgets/private-message-map.js
@@ -1,6 +1,5 @@
import { avatarFor, avatarImg } from "discourse/widgets/post";
import I18n from "I18n";
-import bootbox from "bootbox";
import { createWidget } from "discourse/widgets/widget";
import getURL from "discourse-common/lib/get-url";
import { h } from "virtual-dom";
@@ -10,18 +9,16 @@ import { makeArray } from "discourse-common/lib/helpers";
createWidget("pm-remove-group-link", {
tagName: "a.remove-invited.no-text.btn-icon.btn",
template: hbs`{{d-icon "times"}}`,
+ services: ["dialog"],
click() {
- bootbox.confirm(
- I18n.t("private_message_info.remove_allowed_group", {
+ this.dialog.deleteConfirm({
+ message: I18n.t("private_message_info.remove_allowed_group", {
name: this.attrs.name,
}),
- (confirmed) => {
- if (confirmed) {
- this.sendWidgetAction("removeAllowedGroup", this.attrs);
- }
- }
- );
+ confirmButtonLabel: "private_message_info.remove_group",
+ didConfirm: () => this.sendWidgetAction("removeAllowedGroup", this.attrs),
+ });
},
});
@@ -48,22 +45,23 @@ createWidget("pm-map-user-group", {
createWidget("pm-remove-link", {
tagName: "a.remove-invited.no-text.btn-icon.btn",
template: hbs`{{d-icon "times"}}`,
+ services: ["dialog"],
click() {
const messageKey = this.attrs.isCurrentUser
? "leave_message"
: "remove_allowed_user";
- bootbox.confirm(
- I18n.t(`private_message_info.${messageKey}`, {
+ this.dialog.deleteConfirm({
+ message: I18n.t(`private_message_info.${messageKey}`, {
name: this.attrs.user.username,
}),
- (confirmed) => {
- if (confirmed) {
- this.sendWidgetAction("removeAllowedUser", this.attrs.user);
- }
- }
- );
+ confirmButtonLabel: this.attrs.isCurrentUser
+ ? "private_message_info.leave"
+ : "private_message_info.remove_user",
+ didConfirm: () =>
+ this.sendWidgetAction("removeAllowedUser", this.attrs.user),
+ });
},
});
diff --git a/app/assets/javascripts/discourse/lib/dialog-holder/addon/components/dialog-holder.hbs b/app/assets/javascripts/discourse/lib/dialog-holder/addon/components/dialog-holder.hbs
index 46e2cd5aaf..7a1729f5b1 100644
--- a/app/assets/javascripts/discourse/lib/dialog-holder/addon/components/dialog-holder.hbs
+++ b/app/assets/javascripts/discourse/lib/dialog-holder/addon/components/dialog-holder.hbs
@@ -21,7 +21,7 @@
{{#each this.dialog.buttons as |button|}}
{{else}}
-
+
{{#if this.dialog.shouldDisplayCancel}}
{{/if}}
diff --git a/app/assets/javascripts/discourse/lib/dialog-holder/addon/services/dialog.js b/app/assets/javascripts/discourse/lib/dialog-holder/addon/services/dialog.js
index fa072595ed..824e6e02d3 100644
--- a/app/assets/javascripts/discourse/lib/dialog-holder/addon/services/dialog.js
+++ b/app/assets/javascripts/discourse/lib/dialog-holder/addon/services/dialog.js
@@ -12,6 +12,7 @@ export default Service.extend({
confirmButtonIcon: null,
confirmButtonLabel: null,
+ confirmButtonClass: null,
cancelButtonLabel: null,
shouldDisplayCancel: null,
@@ -29,6 +30,7 @@ export default Service.extend({
confirmButtonIcon,
confirmButtonLabel = "ok_value",
+ confirmButtonClass = "btn-primary",
cancelButtonLabel = "cancel_value",
shouldDisplayCancel,
@@ -47,6 +49,7 @@ export default Service.extend({
title,
titleElementId: title !== null ? "dialog-title" : null,
+ confirmButtonClass,
confirmButtonLabel,
confirmButtonIcon,
cancelButtonLabel,
@@ -108,6 +111,14 @@ export default Service.extend({
});
},
+ deleteConfirm(params) {
+ return this.confirm({
+ ...params,
+ confirmButtonClass: "btn-danger",
+ confirmButtonLabel: params.confirmButtonLabel || "delete",
+ });
+ },
+
reset() {
this.setProperties({
message: null,
diff --git a/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js b/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js
index c60d3170cf..73918cd5e2 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/admin-suspend-user-test.js
@@ -54,9 +54,9 @@ acceptance("Admin - Suspend User", function (needs) {
await click(".d-modal-cancel");
- assert.strictEqual(count(".bootbox.modal:visible"), 1);
+ assert.strictEqual(count(".dialog-body:visible"), 1);
- await click(".modal-footer .btn-default");
+ await click(".dialog-footer .btn-default");
assert.strictEqual(count(".suspend-user-modal:visible"), 1);
assert.strictEqual(
query(".suspend-message").value,
@@ -64,11 +64,11 @@ acceptance("Admin - Suspend User", function (needs) {
);
await click(".d-modal-cancel");
- assert.strictEqual(count(".bootbox.modal:visible"), 1);
+ assert.strictEqual(count(".dialog-body:visible"), 1);
assert.ok(!exists(".suspend-user-modal:visible"));
- await click(".modal-footer .btn-primary");
- assert.ok(!exists(".bootbox.modal:visible"));
+ await click(".dialog-footer .btn-primary");
+ assert.ok(!exists(".dialog-body:visible"));
});
test("suspend, then unsuspend a user", async function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js b/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js
index e46c80057c..e9fb662fd6 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/bookmarks-test.js
@@ -215,15 +215,15 @@ acceptance("Bookmarking", function (needs) {
await click("#delete-bookmark");
- assert.ok(exists(".bootbox.modal"), "it asks for delete confirmation");
+ assert.ok(exists(".dialog-body"), "it asks for delete confirmation");
assert.ok(
- query(".bootbox.modal").innerText.includes(
+ query(".dialog-body").innerText.includes(
I18n.t("bookmarks.confirm_delete")
),
"it shows delete confirmation message"
);
- await click(".bootbox.modal .btn-primary");
+ await click(".dialog-footer .btn-danger");
assert.notOk(
exists(".topic-post:first-child button.bookmark.bookmarked"),
@@ -444,15 +444,15 @@ acceptance("Bookmarking", function (needs) {
await click("#topic-footer-button-bookmark");
await click("#delete-bookmark");
- assert.ok(exists(".bootbox.modal"), "it asks for delete confirmation");
+ assert.ok(exists(".dialog-body"), "it asks for delete confirmation");
assert.ok(
- query(".bootbox.modal").innerText.includes(
+ query(".dialog-body").innerText.includes(
I18n.t("bookmarks.confirm_delete")
),
"it shows delete confirmation message"
);
- await click(".bootbox.modal .btn-primary");
+ await click(".dialog-footer .btn-danger");
assert.strictEqual(
query("#topic-footer-button-bookmark").innerText,
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
index a2002935f0..8be69c4bd6 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-actions-test.js
@@ -150,7 +150,7 @@ acceptance("Composer Actions", function (needs) {
const composerActions = selectKit(".composer-actions");
await composerActions.expand();
await composerActions.selectRowByValue("reply_as_new_topic");
- assert.ok(!exists(".bootbox"));
+ assert.ok(!exists(".dialog-body"));
});
test("reply_as_new_group_message", async function (assert) {
@@ -471,10 +471,10 @@ acceptance("Composer Actions With New Topic Draft", function (needs) {
await composerActions.selectRowByValue("reply_as_new_topic");
assert.strictEqual(
- query(".bootbox .modal-body").innerText,
+ query(".dialog-body").innerText.trim(),
I18n.t("composer.composer_actions.reply_as_new_topic.confirm")
);
- await click(".modal-footer .btn.btn-primary");
+ await click(".dialog-footer .btn-primary");
assert.ok(
query(".d-editor-input").value.startsWith(
diff --git a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
index 98ada74b6d..ab11f39043 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/composer-test.js
@@ -223,7 +223,7 @@ acceptance("Composer", function (needs) {
assert.ok(exists(".d-modal"), "it pops up a confirmation dialog");
await click(".modal-footer .discard-draft");
- assert.ok(!exists(".bootbox.modal"), "the confirmation can be cancelled");
+ assert.ok(!exists(".modal-body"), "the confirmation can be cancelled");
});
test("Create a topic with server side errors", async function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js b/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
index e4cc526ddd..a3b4b04842 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/flag-post-test.js
@@ -1,6 +1,5 @@
import {
acceptance,
- count,
exists,
query,
} from "discourse/tests/helpers/qunit-helpers";
@@ -123,9 +122,11 @@ acceptance("flagging", function (needs) {
const silenceUntilCombobox = selectKit(".silence-until .combobox");
await silenceUntilCombobox.expand();
await silenceUntilCombobox.selectRowByValue("tomorrow");
+ assert.ok(exists(".modal-body"));
await fillIn(".silence-reason", "for breaking the rules");
+
await click(".perform-silence");
- assert.ok(!exists(".bootbox.modal:visible"));
+ assert.ok(!exists(".modal-body"));
});
test("Gets dismissable warning from canceling incomplete silence from take action", async function (assert) {
@@ -140,17 +141,17 @@ acceptance("flagging", function (needs) {
await silenceUntilCombobox.selectRowByValue("tomorrow");
await fillIn(".silence-reason", "for breaking the rules");
await click(".d-modal-cancel");
- assert.strictEqual(count(".bootbox.modal:visible"), 1);
+ assert.ok(exists(".dialog-body"));
- await click(".modal-footer .btn-default");
- assert.ok(!exists(".bootbox.modal:visible"));
+ await click(".dialog-footer .btn-default");
+ assert.ok(!exists(".dialog-body"));
assert.ok(exists(".silence-user-modal"), "it shows the silence modal");
await click(".d-modal-cancel");
- assert.strictEqual(count(".bootbox.modal:visible"), 1);
+ assert.ok(exists(".dialog-body"));
- await click(".modal-footer .btn-primary");
- assert.ok(!exists(".bootbox.modal:visible"));
+ await click(".dialog-footer .btn-primary");
+ assert.ok(!exists(".dialog-body"));
});
test("CTRL + ENTER accepts the modal", async function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js b/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
index f21315b6e8..bbe7bc804b 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/shared-drafts-test.js
@@ -15,7 +15,7 @@ acceptance("Shared Drafts", function () {
assert.strictEqual(categoryChooser.header().value(), "3");
await click(".publish-shared-draft");
- await click(".bootbox .btn-primary");
+ await click(".dialog-footer .btn-primary");
assert.ok(!exists(".shared-draft-controls"));
});
diff --git a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
index 691ecd9ef5..d958162f7b 100644
--- a/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
+++ b/app/assets/javascripts/discourse/tests/acceptance/user-bookmarks-test.js
@@ -20,7 +20,7 @@ acceptance("User's bookmarks", function (needs) {
await dropdown.expand();
await dropdown.selectRowByValue("remove");
- assert.notOk(exists(".bootbox.modal"), "it should not show the modal");
+ assert.notOk(exists(".dialog-body"), "it should not show the modal");
});
test("it renders search controls if there are bookmarks", async function (assert) {
@@ -51,10 +51,10 @@ acceptance("User's bookmarks - reminder", function (needs) {
await dropdown.expand();
await dropdown.selectRowByValue("remove");
- assert.ok(exists(".bootbox.modal"), "it asks for delete confirmation");
+ assert.ok(exists(".dialog-body"), "it asks for delete confirmation");
- await click(".bootbox.modal a.btn-primary");
- assert.notOk(exists(".bootbox.modal"));
+ await click(".dialog-footer .btn-danger");
+ assert.notOk(exists(".dialog-body"));
});
test("bookmarks with reminders have a clear reminder option", async function (assert) {
diff --git a/app/assets/javascripts/discourse/tests/integration/components/dialog-holder-test.js b/app/assets/javascripts/discourse/tests/integration/components/dialog-holder-test.js
index c83889e20f..251e8ac4e2 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/dialog-holder-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/components/dialog-holder-test.js
@@ -364,4 +364,28 @@ module("Integration | Component | dialog-holder", function (hooks) {
assert.notOk(query(".dialog-footer"), "no footer");
assert.notOk(query(".dialog-header"), "no header");
});
+
+ test("delete confirm", async function (assert) {
+ await render(hbs``);
+
+ this.dialog.deleteConfirm({ message: "A delete confirm message" });
+ await settled();
+
+ assert.strictEqual(
+ query(".dialog-body").innerText.trim(),
+ "A delete confirm message",
+ "dialog message is shown"
+ );
+
+ assert.strictEqual(
+ query(".dialog-footer .btn-danger").innerText.trim(),
+ I18n.t("delete"),
+ "dialog primary button use danger class and label is Delete"
+ );
+
+ assert.notOk(
+ query(".dialog-footer .btn-primary"),
+ ".btn-primary element is not present in the dialog"
+ );
+ });
});
diff --git a/app/assets/javascripts/select-kit/addon/components/composer-actions.js b/app/assets/javascripts/select-kit/addon/components/composer-actions.js
index da00b80e31..20b515605e 100644
--- a/app/assets/javascripts/select-kit/addon/components/composer-actions.js
+++ b/app/assets/javascripts/select-kit/addon/components/composer-actions.js
@@ -9,10 +9,10 @@ import discourseComputed from "discourse-common/utils/decorators";
import Draft from "discourse/models/draft";
import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box";
import I18n from "I18n";
-import bootbox from "bootbox";
import { camelize } from "@ember/string";
import { equal, gt } from "@ember/object/computed";
import { isEmpty } from "@ember/utils";
+import { inject as service } from "@ember/service";
// Component can get destroyed and lose state
let _topicSnapshot = null;
@@ -26,6 +26,7 @@ export function _clearSnapshots() {
}
export default DropdownSelectBoxComponent.extend({
+ dialog: service(),
seq: 0,
pluginApiIdentifiers: ["composer-actions"],
classNames: ["composer-actions"],
@@ -283,14 +284,13 @@ export default DropdownSelectBoxComponent.extend({
replyAsNewTopicSelected(options) {
Draft.get("new_topic").then((response) => {
if (response.draft) {
- bootbox.confirm(
- I18n.t("composer.composer_actions.reply_as_new_topic.confirm"),
- (result) => {
- if (result) {
- this._replyAsNewTopicSelect(options);
- }
- }
- );
+ this.dialog.confirm({
+ message: I18n.t(
+ "composer.composer_actions.reply_as_new_topic.confirm"
+ ),
+ confirmButtonLabel: "composer.ok_proceed",
+ didConfirm: () => this._replyAsNewTopicSelect(options),
+ });
} else {
this._replyAsNewTopicSelect(options);
}
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index b8321bc94e..bf30b07949 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -254,6 +254,7 @@ en:
ok_value: "OK"
cancel_value: "Cancel"
submit: "Submit"
+ delete: "Delete"
generic_error: "Sorry, an error has occurred."
generic_error_with_reason: "An error occurred: %{error}"
sign_up: "Sign Up"
@@ -1918,6 +1919,9 @@ en:
leave_message: "Do you really want to leave this message?"
remove_allowed_user: "Do you really want to remove %{name} from this message?"
remove_allowed_group: "Do you really want to remove %{name} from this message?"
+ leave: "Leave"
+ remove_group: "Remove group"
+ remove_user: "Remove user"
email: "Email"
username: "Username"
@@ -2169,6 +2173,7 @@ en:
edit_conflict: "edit conflict"
esc: "esc"
esc_label: "Click or press Esc to dismiss"
+ ok_proceed: "Ok, proceed"
group_mentioned_limit:
one: "Warning! You mentioned %{group}, however this group has more members than the administrator configured mention limit of %{count} user. Nobody will be notified."
@@ -2669,7 +2674,7 @@ en:
remove_tags: "Remove All Tags"
confirm_remove_tags:
one: "All tags will be removed from this topic. Are you sure?"
- other: "All tags will be removed from %{count} topics. Are you sure?"
+ other: "All tags will be removed from %{count} topics. Are you sure?"
progress:
one: "Progress: %{count} topic"
other: "Progress: %{count} topics"
@@ -3998,7 +4003,6 @@ en:
tag_list_joiner: ", "
delete_unused: "Delete Unused Tags"
delete_unused_description: "Delete all tags which are not attached to any topics or personal messages"
- cancel_delete_unused: "Cancel"
filters:
without_category: "%{filter} %{tag} topics"
with_category: "%{filter} %{tag} topics in %{category}"