DEV: Refactor the last few bootbox dialogs (#18416)

This commit is contained in:
Penar Musaraj 2022-10-07 11:38:27 -04:00 committed by GitHub
parent 6426932834
commit a1d67122b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 331 additions and 333 deletions

View File

@ -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);
});
}
},
});
},

View File

@ -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);
},
});
},
});

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
},
});
}
},
},

View File

@ -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();
},
});
}
},
},

View File

@ -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),
});
}
},

View File

@ -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 });

View File

@ -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",
});
});
});

View File

@ -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),
});
});
},

View File

@ -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();

View File

@ -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", {

View File

@ -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);
});
}
},
});
},
},

View File

@ -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);
}
);
},
});
},
},
});

View File

@ -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() {

View File

@ -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));
},
});
},

View File

@ -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")));
},
});
},

View File

@ -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);
},

View File

@ -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" }),
});
},
},
});

View File

@ -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);
}
},
});
},

View File

@ -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),
});
},
});

View File

@ -21,7 +21,7 @@
{{#each this.dialog.buttons as |button|}}
<DButton @icon={{button.icon}} @class={{button.class}} @action={{action "handleButtonAction" button}} @translatedLabel={{button.label}} />
{{else}}
<DButton @class="btn-primary" @action={{this.dialog.didConfirmWrapped}} @icon={{this.dialog.confirmButtonIcon}} @label={{this.dialog.confirmButtonLabel}} />
<DButton @class={{this.dialog.confirmButtonClass}} @action={{this.dialog.didConfirmWrapped}} @icon={{this.dialog.confirmButtonIcon}} @label={{this.dialog.confirmButtonLabel}} />
{{#if this.dialog.shouldDisplayCancel}}
<DButton @class="btn-default" @action={{this.dialog.cancel}} @label={{this.dialog.cancelButtonLabel}} />
{{/if}}

View File

@ -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,

View File

@ -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) {

View File

@ -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,

View File

@ -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(

View File

@ -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) {

View File

@ -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) {

View File

@ -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"));
});

View File

@ -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) {

View File

@ -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`<DialogHolder />`);
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"
);
});
});

View File

@ -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);
}

View File

@ -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: "<b>Warning!</b> You mentioned <a href='%{group_link}'>%{group}</a>, 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 <b>%{count}</b> topics. Are you sure?"
other: "All tags will be removed from %{count} topics. Are you sure?"
progress:
one: "Progress: <strong>%{count}</strong> topic"
other: "Progress: <strong>%{count}</strong> 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}"