DEV: Move modal hide/show logic into a service (#21748)

For now, state is still stored in the modal controller. Eventually the controller will be replaced with a component, and the state will be stored in the service.

(extracted from https://github.com/discourse/discourse/pull/21304)
This commit is contained in:
David Taylor 2023-05-25 13:47:44 +01:00 committed by GitHub
parent db54b80275
commit d460309f89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 120 deletions

View File

@ -1,5 +1,3 @@
import I18n from "I18n";
import { dasherize } from "@ember/string";
import { getOwner } from "discourse-common/lib/get-owner";
/**
@ -26,80 +24,6 @@ export default function showModal(name, opts) {
return;
}
// We use the container here because modals are like singletons
// in Discourse. Only one can be shown with a particular state.
const route = container.lookup("route:application");
const modalController = route.controllerFor("modal");
modalController.set(
"modalClass",
opts.modalClass || `${dasherize(name).toLowerCase()}-modal`
);
const controllerName = opts.admin ? `modals/${name}` : name;
modalController.set("name", controllerName);
let controller = container.lookup("controller:" + controllerName);
const templateName = opts.templateName || dasherize(name);
const renderArgs = { into: "modal", outlet: "modalBody" };
if (controller) {
renderArgs.controller = controllerName;
} else {
// use a basic controller
renderArgs.controller = "basic-modal-body";
controller = container.lookup(`controller:${renderArgs.controller}`);
}
if (opts.addModalBodyView) {
renderArgs.view = "modal-body";
}
const modalName = `modal/${templateName}`;
const fullName = opts.admin ? `admin/templates/${modalName}` : modalName;
route.render(fullName, renderArgs);
if (opts.title) {
modalController.set("title", I18n.t(opts.title));
} else if (opts.titleTranslated) {
modalController.set("title", opts.titleTranslated);
} else {
modalController.set("title", null);
}
if (opts.titleAriaElementId) {
modalController.set("titleAriaElementId", opts.titleAriaElementId);
}
if (opts.panels) {
modalController.setProperties({
panels: opts.panels,
selectedPanel: opts.panels[0],
});
if (controller.actions.onSelectPanel) {
modalController.set(
"onSelectPanel",
controller.actions.onSelectPanel.bind(controller)
);
}
modalController.set(
"modalClass",
`${modalController.get("modalClass")} has-tabs`
);
} else {
modalController.setProperties({ panels: [], selectedPanel: null });
}
controller.set("modal", modalController);
const model = opts.model;
if (model) {
controller.set("model", model);
}
if (controller.onShow) {
controller.onShow();
}
controller.set("flashMessage", null);
return controller;
const modalService = container.lookup("service:modal");
return modalService.show(name, opts);
}

View File

@ -40,6 +40,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
documentTitle: service(),
dialog: service(),
composer: service(),
modal: service(),
actions: {
toggleAnonymous() {
@ -152,46 +153,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
// Close the current modal, and destroy its state.
closeModal(initiatedBy) {
const route = getOwner(this).lookup("route:application");
let modalController = route.controllerFor("modal");
const controllerName = modalController.get("name");
if (controllerName) {
const controller = getOwner(this).lookup(
`controller:${controllerName}`
);
if (controller && controller.beforeClose) {
if (false === controller.beforeClose()) {
return;
}
}
}
this.render("hide-modal", { into: "modal", outlet: "modalBody" });
$(".d-modal.fixed-modal").modal("hide");
if (controllerName) {
const controller = getOwner(this).lookup(
`controller:${controllerName}`
);
if (controller) {
this.appEvents.trigger("modal:closed", {
name: controllerName,
controller,
});
if (controller.onClose) {
controller.onClose({
initiatedByCloseButton: initiatedBy === "initiatedByCloseButton",
initiatedByClickOut: initiatedBy === "initiatedByClickOut",
initiatedByESC: initiatedBy === "initiatedByESC",
});
}
}
modalController.set("name", null);
}
modalController.hidden = true;
return this.modal.close(initiatedBy);
},
/**
@ -200,11 +162,11 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
user clicks "No", reopenModal. If user clicks "Yes", be sure to call closeModal.
**/
hideModal() {
$(".d-modal.fixed-modal").modal("hide");
return this.modal.hide();
},
reopenModal() {
$(".d-modal.fixed-modal").modal("show");
return this.modal.reopen();
},
editCategory(category) {

View File

@ -0,0 +1,137 @@
import Service, { inject as service } from "@ember/service";
import { getOwner } from "@ember/application";
import I18n from "I18n";
import { dasherize } from "@ember/string";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
@disableImplicitInjections
export default class ModalService extends Service {
@service appEvents;
show(name, opts = {}) {
const container = getOwner(this);
const route = container.lookup("route:application");
const modalController = route.controllerFor("modal");
modalController.set(
"modalClass",
opts.modalClass || `${dasherize(name).toLowerCase()}-modal`
);
const controllerName = opts.admin ? `modals/${name}` : name;
modalController.set("name", controllerName);
let controller = container.lookup("controller:" + controllerName);
const templateName = opts.templateName || dasherize(name);
const renderArgs = { into: "modal", outlet: "modalBody" };
if (controller) {
renderArgs.controller = controllerName;
} else {
// use a basic controller
renderArgs.controller = "basic-modal-body";
controller = container.lookup(`controller:${renderArgs.controller}`);
}
if (opts.addModalBodyView) {
renderArgs.view = "modal-body";
}
const modalName = `modal/${templateName}`;
const fullName = opts.admin ? `admin/templates/${modalName}` : modalName;
route.render(fullName, renderArgs);
if (opts.title) {
modalController.set("title", I18n.t(opts.title));
} else if (opts.titleTranslated) {
modalController.set("title", opts.titleTranslated);
} else {
modalController.set("title", null);
}
if (opts.titleAriaElementId) {
modalController.set("titleAriaElementId", opts.titleAriaElementId);
}
if (opts.panels) {
modalController.setProperties({
panels: opts.panels,
selectedPanel: opts.panels[0],
});
if (controller.actions.onSelectPanel) {
modalController.set(
"onSelectPanel",
controller.actions.onSelectPanel.bind(controller)
);
}
modalController.set(
"modalClass",
`${modalController.get("modalClass")} has-tabs`
);
} else {
modalController.setProperties({ panels: [], selectedPanel: null });
}
controller.set("modal", modalController);
const model = opts.model;
if (model) {
controller.set("model", model);
}
if (controller.onShow) {
controller.onShow();
}
controller.set("flashMessage", null);
return controller;
}
close(initiatedBy) {
const route = getOwner(this).lookup("route:application");
let modalController = route.controllerFor("modal");
const controllerName = modalController.get("name");
if (controllerName) {
const controller = getOwner(this).lookup(`controller:${controllerName}`);
if (controller && controller.beforeClose) {
if (false === controller.beforeClose()) {
return;
}
}
}
getOwner(this)
.lookup("route:application")
.render("hide-modal", { into: "modal", outlet: "modalBody" });
$(".d-modal.fixed-modal").modal("hide");
if (controllerName) {
const controller = getOwner(this).lookup(`controller:${controllerName}`);
if (controller) {
this.appEvents.trigger("modal:closed", {
name: controllerName,
controller,
});
if (controller.onClose) {
controller.onClose({
initiatedByCloseButton: initiatedBy === "initiatedByCloseButton",
initiatedByClickOut: initiatedBy === "initiatedByClickOut",
initiatedByESC: initiatedBy === "initiatedByESC",
});
}
}
modalController.set("name", null);
}
modalController.hidden = true;
}
hide() {
$(".d-modal.fixed-modal").modal("hide");
}
reopen() {
$(".d-modal.fixed-modal").modal("show");
}
}