diff --git a/app/assets/javascripts/discourse/app/components/discovery/navigation.js b/app/assets/javascripts/discourse/app/components/discovery/navigation.js
index bde1de83439..f6762f8d75f 100644
--- a/app/assets/javascripts/discourse/app/components/discovery/navigation.js
+++ b/app/assets/javascripts/discourse/app/components/discovery/navigation.js
@@ -1,8 +1,8 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
+import ReorderCategories from "discourse/components/modal/reorder-categories";
import { calculateFilterMode } from "discourse/lib/filter-mode";
-import showModal from "discourse/lib/show-modal";
import { TRACKED_QUERY_PARAM_VALUE } from "discourse/lib/topic-list-tracked-filter";
import DiscourseURL from "discourse/lib/url";
import Category from "discourse/models/category";
@@ -56,6 +56,6 @@ export default class DiscoveryNavigation extends Component {
@action
reorderCategories() {
- showModal("reorder-categories");
+ this.modal.show(ReorderCategories);
}
}
diff --git a/app/assets/javascripts/discourse/app/components/modal/reorder-categories.hbs b/app/assets/javascripts/discourse/app/components/modal/reorder-categories.hbs
new file mode 100644
index 00000000000..06c04f940bc
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/modal/reorder-categories.hbs
@@ -0,0 +1,53 @@
+
+ <:body>
+
+
+ {{i18n "categories.category"}} |
+ {{i18n "categories.reorder.position"}} |
+
+
+ {{#each this.categoriesOrdered as |category|}}
+
+
+
+ {{category-badge category allowUncategorized="true"}}
+
+ |
+
+
+
+
+
+
+
+ |
+
+ {{/each}}
+
+
+
+
+ <:footer>
+
+
+
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/controllers/reorder-categories.js b/app/assets/javascripts/discourse/app/components/modal/reorder-categories.js
similarity index 55%
rename from app/assets/javascripts/discourse/app/controllers/reorder-categories.js
rename to app/assets/javascripts/discourse/app/components/modal/reorder-categories.js
index b339baab102..9a06bb59ff9 100644
--- a/app/assets/javascripts/discourse/app/controllers/reorder-categories.js
+++ b/app/assets/javascripts/discourse/app/components/modal/reorder-categories.js
@@ -1,24 +1,22 @@
-import Controller from "@ember/controller";
+import Component from "@ember/component";
+import { action } from "@ember/object";
import { sort } from "@ember/object/computed";
-import Evented from "@ember/object/evented";
-import BufferedProxy from "ember-buffered-proxy/proxy";
+import { next } from "@ember/runloop";
+import { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
-import ModalFunctionality from "discourse/mixins/modal-functionality";
-import discourseComputed, { on } from "discourse-common/utils/decorators";
-export default Controller.extend(ModalFunctionality, Evented, {
+export default class ReorderCategories extends Component {
+ @service site;
+
+ categoriesSorting = ["position"];
+
+ @sort("site.categories", "categoriesSorting") categoriesOrdered;
+
init() {
- this._super(...arguments);
- this.categoriesSorting = ["position"];
- },
-
- @discourseComputed("site.categories.[]")
- categoriesBuffered(categories) {
- return (categories || []).map((c) => BufferedProxy.create({ content: c }));
- },
-
- categoriesOrdered: sort("categoriesBuffered", "categoriesSorting"),
+ super.init(...arguments);
+ next(() => this.reorder());
+ }
/**
* 1. Make sure all categories have unique position numbers.
@@ -31,46 +29,39 @@ export default Controller.extend(ModalFunctionality, Evented, {
* parent => parent/c2
* other parent/c2/c1
* parent/c2 other
- *
**/
- @on("init")
reorder() {
- const reorderChildren = (categoryId, depth, index) => {
- this.categoriesOrdered.forEach((category) => {
- if (
- (categoryId === null && !category.get("parent_category_id")) ||
- category.get("parent_category_id") === categoryId
- ) {
- category.setProperties({ depth, position: index++ });
- index = reorderChildren(category.get("id"), depth + 1, index);
- }
- });
+ this.reorderChildren(null, 0, 0);
+ }
- return index;
- };
-
- reorderChildren(null, 0, 0);
-
- this.categoriesBuffered.forEach((bc) => {
- if (bc.get("hasBufferedChanges")) {
- bc.applyBufferedChanges();
+ reorderChildren(categoryId, depth, index) {
+ for (const category of this.categoriesOrdered) {
+ if (
+ (categoryId === null && !category.get("parent_category_id")) ||
+ category.get("parent_category_id") === categoryId
+ ) {
+ category.setProperties({ depth, position: index++ });
+ index = this.reorderChildren(category.get("id"), depth + 1, index);
}
- });
+ }
- this.notifyPropertyChange("categoriesBuffered");
- },
+ return index;
+ }
countDescendants(category) {
- return category.get("subcategories")
- ? category
- .get("subcategories")
- .reduce(
- (count, subcategory) => count + this.countDescendants(subcategory),
- category.get("subcategories").length
- )
- : 0;
- },
+ if (!category.get("subcategories")) {
+ return 0;
+ }
+ return category
+ .get("subcategories")
+ .reduce(
+ (count, subcategory) => count + this.countDescendants(subcategory),
+ category.get("subcategories").length
+ );
+ }
+
+ @action
move(category, direction) {
let targetPosition = category.get("position") + direction;
@@ -114,7 +105,7 @@ export default Controller.extend(ModalFunctionality, Evented, {
}
// Update other categories between current and target position
- this.categoriesOrdered.map((c) => {
+ for (const c of this.categoriesOrdered) {
if (direction < 0) {
// Moving up (position gets smaller)
if (
@@ -134,47 +125,42 @@ export default Controller.extend(ModalFunctionality, Evented, {
c.set("position", newPosition);
}
}
- });
+ }
// Update this category's position to target position
category.set("position", targetPosition);
this.reorder();
- },
+ }
- actions: {
- change(category, event) {
- let newPosition = parseFloat(event.target.value);
- newPosition =
- newPosition < category.get("position")
- ? Math.ceil(newPosition)
- : Math.floor(newPosition);
- const direction = newPosition - category.get("position");
- this.move(category, direction);
- },
+ @action
+ change(category, event) {
+ let newPosition = parseFloat(event.target.value);
+ newPosition =
+ newPosition < category.get("position")
+ ? Math.ceil(newPosition)
+ : Math.floor(newPosition);
+ const direction = newPosition - category.get("position");
+ this.move(category, direction);
+ }
- moveUp(category) {
- this.move(category, -1);
- },
+ @action
+ async save() {
+ this.reorder();
- moveDown(category) {
- this.move(category, 1);
- },
+ const data = {};
+ for (const category of this.site.categories) {
+ data[category.get("id")] = category.get("position");
+ }
- save() {
- this.reorder();
-
- const data = {};
- this.categoriesBuffered.forEach((cat) => {
- data[cat.get("id")] = cat.get("position");
- });
-
- ajax("/categories/reorder", {
+ try {
+ await ajax("/categories/reorder", {
type: "POST",
data: { mapping: JSON.stringify(data) },
- })
- .then(() => window.location.reload())
- .catch(popupAjaxError);
- },
- },
-});
+ });
+ window.location.reload();
+ } catch (e) {
+ popupAjaxError(e);
+ }
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/routes/discovery-categories.js b/app/assets/javascripts/discourse/app/routes/discovery-categories.js
index 83dafac22ce..f5f8960f88b 100644
--- a/app/assets/javascripts/discourse/app/routes/discovery-categories.js
+++ b/app/assets/javascripts/discourse/app/routes/discovery-categories.js
@@ -10,6 +10,7 @@ import DiscourseRoute from "discourse/routes/discourse";
import I18n from "discourse-i18n";
export default class DiscoveryCategoriesRoute extends DiscourseRoute {
+ @service modal;
@service router;
@service session;
diff --git a/app/assets/javascripts/discourse/app/services/modal.js b/app/assets/javascripts/discourse/app/services/modal.js
index bfc5ea96d0e..19c2c7777b6 100644
--- a/app/assets/javascripts/discourse/app/services/modal.js
+++ b/app/assets/javascripts/discourse/app/services/modal.js
@@ -11,20 +11,6 @@ import deprecated, {
} from "discourse-common/lib/deprecated";
import I18n from "discourse-i18n";
-// Known legacy modals in core. Silence deprecation warnings for these so the messages
-// don't cause unnecessary noise.
-const KNOWN_LEGACY_MODALS = [
- "avatar-selector",
- "change-owner",
- "change-post-notice",
- "create-invite-bulk",
- "create-invite",
- "grant-badge",
- "group-default-notifications",
- "reject-reason-reviewable",
- "reorder-categories",
-];
-
const LEGACY_OPTS = new Set([
"admin",
"templateName",
@@ -139,17 +125,15 @@ export default class ModalServiceWithLegacySupport extends ModalService {
this.close({ initiatedBy: CLOSE_INITIATED_BY_MODAL_SHOW });
- if (!KNOWN_LEGACY_MODALS.includes(modal)) {
- deprecated(
- `Defining modals using a controller is deprecated. Use the component-based API instead. (modal: ${modal})`,
- {
- id: "discourse.modal-controllers",
- since: "3.1",
- dropFrom: "3.2",
- url: "https://meta.discourse.org/t/268057",
- }
- );
- }
+ deprecated(
+ `Defining modals using a controller is deprecated. Use the component-based API instead. (modal: ${modal})`,
+ {
+ id: "discourse.modal-controllers",
+ since: "3.1",
+ dropFrom: "3.2",
+ url: "https://meta.discourse.org/t/268057",
+ }
+ );
const name = modal;
const container = getOwner(this);
diff --git a/app/assets/javascripts/discourse/app/templates/modal/reorder-categories.hbs b/app/assets/javascripts/discourse/app/templates/modal/reorder-categories.hbs
deleted file mode 100644
index 58bb885f9c6..00000000000
--- a/app/assets/javascripts/discourse/app/templates/modal/reorder-categories.hbs
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
- {{i18n "categories.category"}} |
- {{i18n "categories.reorder.position"}} |
-
-
- {{#each this.categoriesOrdered as |cat|}}
-
-
-
- {{category-badge cat allowUncategorized="true"}}
-
- |
-
-
-
-
-
- |
-
- {{/each}}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/tests/unit/controllers/reorder-categories-test.js b/app/assets/javascripts/discourse/tests/unit/components/reorder-categories-test.js
similarity index 79%
rename from app/assets/javascripts/discourse/tests/unit/controllers/reorder-categories-test.js
rename to app/assets/javascripts/discourse/tests/unit/components/reorder-categories-test.js
index 1456adc9da6..941a44a1b02 100644
--- a/app/assets/javascripts/discourse/tests/unit/controllers/reorder-categories-test.js
+++ b/app/assets/javascripts/discourse/tests/unit/components/reorder-categories-test.js
@@ -2,11 +2,13 @@ import { getOwner } from "@ember/application";
import { setupTest } from "ember-qunit";
import { module, test } from "qunit";
-module("Unit | Controller | reorder-categories", function (hooks) {
+module("Unit | Component | reorder-categories", function (hooks) {
setupTest(hooks);
test("reorder set unique position number", function (assert) {
- const controller = getOwner(this).lookup("controller:reorder-categories");
+ const component = this.owner
+ .factoryFor("component:modal/reorder-categories")
+ .create();
const store = getOwner(this).lookup("service:store");
const site = getOwner(this).lookup("service:site");
@@ -16,15 +18,17 @@ module("Unit | Controller | reorder-categories", function (hooks) {
store.createRecord("category", { id: 3, position: 0 }),
]);
- controller.reorder();
+ component.reorder();
- controller.categoriesOrdered.forEach((category, index) => {
+ component.categoriesOrdered.forEach((category, index) => {
assert.strictEqual(category.get("position"), index);
});
});
test("reorder places subcategories after their parent categories, while maintaining the relative order", function (assert) {
- const controller = getOwner(this).lookup("controller:reorder-categories");
+ const component = this.owner
+ .factoryFor("component:modal/reorder-categories")
+ .create();
const store = getOwner(this).lookup("service:store");
const parent = store.createRecord("category", {
@@ -54,16 +58,18 @@ module("Unit | Controller | reorder-categories", function (hooks) {
const site = getOwner(this).lookup("service:site");
site.set("categories", [child2, parent, other, child1]);
- controller.reorder();
+ component.reorder();
assert.deepEqual(
- controller.categoriesOrdered.mapBy("slug"),
+ component.categoriesOrdered.mapBy("slug"),
expectedOrderSlugs
);
});
test("changing the position number of a category should place it at given position", function (assert) {
- const controller = getOwner(this).lookup("controller:reorder-categories");
+ const component = this.owner
+ .factoryFor("component:modal/reorder-categories")
+ .create();
const store = getOwner(this).lookup("service:store");
const elem1 = store.createRecord("category", {
@@ -88,9 +94,9 @@ module("Unit | Controller | reorder-categories", function (hooks) {
site.set("categories", [elem1, elem2, elem3]);
// Move category 'foo' from position 0 to position 2
- controller.send("change", elem1, { target: { value: "2" } });
+ component.change(elem1, { target: { value: "2" } });
- assert.deepEqual(controller.categoriesOrdered.mapBy("slug"), [
+ assert.deepEqual(component.categoriesOrdered.mapBy("slug"), [
"bar",
"test",
"foo",
@@ -98,7 +104,9 @@ module("Unit | Controller | reorder-categories", function (hooks) {
});
test("changing the position number of a category should place it at given position and respect children", function (assert) {
- const controller = getOwner(this).lookup("controller:reorder-categories");
+ const component = this.owner
+ .factoryFor("component:modal/reorder-categories")
+ .create();
const store = getOwner(this).lookup("service:store");
const elem1 = store.createRecord("category", {
@@ -129,9 +137,9 @@ module("Unit | Controller | reorder-categories", function (hooks) {
const site = getOwner(this).lookup("service:site");
site.set("categories", [elem1, child1, elem2, elem3]);
- controller.send("change", elem1, { target: { value: 3 } });
+ component.change(elem1, { target: { value: 3 } });
- assert.deepEqual(controller.categoriesOrdered.mapBy("slug"), [
+ assert.deepEqual(component.categoriesOrdered.mapBy("slug"), [
"bar",
"test",
"foo",
@@ -140,7 +148,9 @@ module("Unit | Controller | reorder-categories", function (hooks) {
});
test("changing the position through click on arrow of a category should place it at given position and respect children", function (assert) {
- const controller = getOwner(this).lookup("controller:reorder-categories");
+ const component = this.owner
+ .factoryFor("component:modal/reorder-categories")
+ .create();
const store = getOwner(this).lookup("service:store");
const child2 = store.createRecord("category", {
@@ -180,11 +190,11 @@ module("Unit | Controller | reorder-categories", function (hooks) {
const site = getOwner(this).lookup("service:site");
site.set("categories", [elem1, child1, child2, elem2, elem3]);
- controller.reorder();
+ component.reorder();
- controller.send("moveDown", elem1);
+ component.move(elem1, 1);
- assert.deepEqual(controller.categoriesOrdered.mapBy("slug"), [
+ assert.deepEqual(component.categoriesOrdered.mapBy("slug"), [
"bar",
"foo",
"foo-child",
diff --git a/app/assets/stylesheets/common/base/_index.scss b/app/assets/stylesheets/common/base/_index.scss
index a77ffbd4ce9..69bf3ffd132 100644
--- a/app/assets/stylesheets/common/base/_index.scss
+++ b/app/assets/stylesheets/common/base/_index.scss
@@ -3,7 +3,7 @@
@import "activation";
@import "alert";
@import "bbcode";
-@import "cat_reorder";
+@import "reorder-categories";
@import "category-list";
@import "code_highlighting";
@import "colorpicker";
diff --git a/app/assets/stylesheets/common/base/cat_reorder.scss b/app/assets/stylesheets/common/base/reorder-categories.scss
similarity index 89%
rename from app/assets/stylesheets/common/base/cat_reorder.scss
rename to app/assets/stylesheets/common/base/reorder-categories.scss
index ac50234a654..e325842aa96 100644
--- a/app/assets/stylesheets/common/base/cat_reorder.scss
+++ b/app/assets/stylesheets/common/base/reorder-categories.scss
@@ -5,16 +5,15 @@
}
}
input[type="text"] {
+ margin: 0;
max-width: 2.5em;
padding: 0.35em;
text-align: center;
+
@include breakpoint(mobile-extra-large) {
width: 2em;
}
}
- #rc-scroll-anchor {
- padding: 0;
- }
table {
padding-bottom: 150px;
margin: 0 0.667em;
@@ -34,6 +33,11 @@
}
}
+.reorder-categories-actions {
+ display: flex;
+ gap: 0.5rem;
+}
+
.reorder-categories-depth-1 {
margin-left: 20px;
}