From d5107d1abaff3c6b5f2c02156e1783f9daa45a7b Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 2 Aug 2023 17:39:11 +0100 Subject: [PATCH] DEV: Refactor build-category-route to define abstract controller This makes more sense (and is likely faster) than redefining the entire route for every call to `buildCategoryRoute` --- .../initializers/dynamic-route-builders.js | 17 +- .../app/routes/build-category-route.js | 415 +++++++++--------- 2 files changed, 217 insertions(+), 215 deletions(-) diff --git a/app/assets/javascripts/discourse/app/initializers/dynamic-route-builders.js b/app/assets/javascripts/discourse/app/initializers/dynamic-route-builders.js index 356f7ca67f7..7839a45c93f 100644 --- a/app/assets/javascripts/discourse/app/initializers/dynamic-route-builders.js +++ b/app/assets/javascripts/discourse/app/initializers/dynamic-route-builders.js @@ -23,18 +23,17 @@ export default { DiscoverySortableController.extend() ); - app.register("route:discovery.category", buildCategoryRoute("default")); + app.register( + "route:discovery.category", + buildCategoryRoute({ filter: "default" }) + ); app.register( "route:discovery.category-none", - buildCategoryRoute("default", { - no_subcategories: true, - }) + buildCategoryRoute({ filter: "default", no_subcategories: true }) ); app.register( "route:discovery.category-all", - buildCategoryRoute("default", { - no_subcategories: false, - }) + buildCategoryRoute({ filter: "default", no_subcategories: false }) ); const site = Site.current(); @@ -83,11 +82,11 @@ export default { app.register( `route:discovery.${filterDasherized}-category`, - buildCategoryRoute(filter) + buildCategoryRoute({ filter }) ); app.register( `route:discovery.${filterDasherized}-category-none`, - buildCategoryRoute(filter, { no_subcategories: true }) + buildCategoryRoute({ filter, no_subcategories: true }) ); }); diff --git a/app/assets/javascripts/discourse/app/routes/build-category-route.js b/app/assets/javascripts/discourse/app/routes/build-category-route.js index 0cee1a6a0c3..c94558dd080 100644 --- a/app/assets/javascripts/discourse/app/routes/build-category-route.js +++ b/app/assets/javascripts/discourse/app/routes/build-category-route.js @@ -18,240 +18,243 @@ import { action } from "@ember/object"; import PreloadStore from "discourse/lib/preload-store"; import { inject as service } from "@ember/service"; -// A helper function to create a category route with parameters -export default (filterArg, params) => { - return DiscourseRoute.extend({ - queryParams, - composer: service(), +const AbstractCategoryRoute = DiscourseRoute.extend({ + queryParams, + composer: service(), - model(modelParams) { - const category = Category.findBySlugPathWithID( + model(modelParams) { + const category = Category.findBySlugPathWithID( + modelParams.category_slug_path_with_id + ); + + if (!category) { + const parts = modelParams.category_slug_path_with_id.split("/"); + if (parts.length > 0 && parts[parts.length - 1].match(/^\d+$/)) { + parts.pop(); + } + + return Category.reloadBySlugPath(parts.join("/")).then((result) => { + const record = this.store.createRecord("category", result.category); + record.setupGroupsAndPermissions(); + this.site.updateCategory(record); + return { category: record, modelParams }; + }); + } + + if (category) { + return { category, modelParams }; + } + }, + + afterModel(model, transition) { + if (!model) { + this.replaceWith("/404"); + return; + } + + const { category, modelParams } = model; + + if ( + this.routeConfig?.no_subcategories === undefined && + category.default_list_filter === "none" && + this.routeConfig?.filter === "default" && + modelParams + ) { + // TODO: avoid throwing away preload data by redirecting on the server + PreloadStore.getAndRemove("topic_list"); + return this.replaceWith( + "discovery.categoryNone", modelParams.category_slug_path_with_id ); + } - if (!category) { - const parts = modelParams.category_slug_path_with_id.split("/"); - if (parts.length > 0 && parts[parts.length - 1].match(/^\d+$/)) { - parts.pop(); - } + this._setupNavigation(category); + return all([ + this._createSubcategoryList(category), + this._retrieveTopicList(category, transition, modelParams), + ]); + }, - return Category.reloadBySlugPath(parts.join("/")).then((result) => { - const record = this.store.createRecord("category", result.category); - record.setupGroupsAndPermissions(); - this.site.updateCategory(record); - return { category: record, modelParams }; - }); - } + filter(category) { + return this.routeConfig?.filter === "default" + ? category.get("default_view") || "latest" + : this.routeConfig?.filter; + }, - if (category) { - return { category, modelParams }; - } - }, + _setupNavigation(category) { + const noSubcategories = + this.routeConfig && !!this.routeConfig.no_subcategories, + filterType = this.filter(category).split("/")[0]; - afterModel(model, transition) { - if (!model) { - this.replaceWith("/404"); - return; - } + this.controllerFor("navigation/category").setProperties({ + category, + filterType, + noSubcategories, + }); + }, - const { category, modelParams } = model; + _createSubcategoryList(category) { + this._categoryList = null; - if ( - (!params || params.no_subcategories === undefined) && - category.default_list_filter === "none" && - filterArg === "default" && - modelParams - ) { - // TODO: avoid throwing away preload data by redirecting on the server - PreloadStore.getAndRemove("topic_list"); - return this.replaceWith( - "discovery.categoryNone", - modelParams.category_slug_path_with_id - ); - } - - this._setupNavigation(category); - return all([ - this._createSubcategoryList(category), - this._retrieveTopicList(category, transition, modelParams), - ]); - }, - - filter(category) { - return filterArg === "default" - ? category.get("default_view") || "latest" - : filterArg; - }, - - _setupNavigation(category) { - const noSubcategories = params && !!params.no_subcategories, - filterType = this.filter(category).split("/")[0]; - - this.controllerFor("navigation/category").setProperties({ - category, - filterType, - noSubcategories, - }); - }, - - _createSubcategoryList(category) { - this._categoryList = null; - - if (category.isParent && category.show_subcategory_list) { - return CategoryList.listForParent(this.store, category).then( - (list) => (this._categoryList = list) - ); - } - - // If we're not loading a subcategory list just resolve - return Promise.resolve(); - }, - - _retrieveTopicList(category, transition, modelParams) { - const findOpts = filterQueryParams(modelParams, params); - const extras = { cached: this.isPoppedState(transition) }; - - let listFilter = `c/${Category.slugFor(category)}/${category.id}`; - if (findOpts.no_subcategories) { - listFilter += "/none"; - } - listFilter += `/l/${this.filter(category)}`; - - return findTopicList( - this.store, - this.topicTrackingState, - listFilter, - findOpts, - extras - ).then((list) => { - TopicList.hideUniformCategory(list, category); - this.set("topics", list); - return list; - }); - }, - - titleToken() { - const category = this.currentModel.category; - - const filterText = I18n.t( - "filters." + this.filter(category).replace("/", ".") + ".title" + if (category.isParent && category.show_subcategory_list) { + return CategoryList.listForParent(this.store, category).then( + (list) => (this._categoryList = list) ); + } - let categoryName = category.name; - if (category.parent_category_id) { - const list = Category.list(); - const parentCategory = list.findBy("id", category.parent_category_id); - categoryName = `${parentCategory.name}/${categoryName}`; - } + // If we're not loading a subcategory list just resolve + return Promise.resolve(); + }, - return I18n.t("filters.with_category", { - filter: filterText, - category: categoryName, - }); - }, + _retrieveTopicList(category, transition, modelParams) { + const findOpts = filterQueryParams(modelParams, this.routeConfig); + const extras = { cached: this.isPoppedState(transition) }; - setupController(controller, model) { - const topics = this.topics, - category = model.category, - canCreateTopic = topics.get("can_create_topic"); + let listFilter = `c/${Category.slugFor(category)}/${category.id}`; + if (findOpts.no_subcategories) { + listFilter += "/none"; + } + listFilter += `/l/${this.filter(category)}`; - let canCreateTopicOnCategory = - canCreateTopic && category.get("permission") === PermissionType.FULL; - let cannotCreateTopicOnCategory = !canCreateTopicOnCategory; - let defaultSubcategory; - let canCreateTopicOnSubCategory; + return findTopicList( + this.store, + this.topicTrackingState, + listFilter, + findOpts, + extras + ).then((list) => { + TopicList.hideUniformCategory(list, category); + this.set("topics", list); + return list; + }); + }, - if (this.siteSettings.default_subcategory_on_read_only_category) { - cannotCreateTopicOnCategory = false; + titleToken() { + const category = this.currentModel.category; - if (!canCreateTopicOnCategory && category.subcategories) { - defaultSubcategory = category.subcategories.find((subcategory) => { - return subcategory.get("permission") === PermissionType.FULL; - }); - canCreateTopicOnSubCategory = !!defaultSubcategory; - } - } + const filterText = I18n.t( + "filters." + this.filter(category).replace("/", ".") + ".title" + ); - this.controllerFor("navigation/category").setProperties({ - canCreateTopicOnCategory, - cannotCreateTopicOnCategory, - canCreateTopic, - canCreateTopicOnSubCategory, - defaultSubcategory, - }); + let categoryName = category.name; + if (category.parent_category_id) { + const list = Category.list(); + const parentCategory = list.findBy("id", category.parent_category_id); + categoryName = `${parentCategory.name}/${categoryName}`; + } - let topicOpts = { - model: topics, - category, - period: - topics.get("for_period") || - (model.modelParams && model.modelParams.period), - selected: [], - noSubcategories: params && !!params.no_subcategories, - expandAllPinned: true, - canCreateTopic, - canCreateTopicOnCategory, - canCreateTopicOnSubCategory, - defaultSubcategory, - }; + return I18n.t("filters.with_category", { + filter: filterText, + category: categoryName, + }); + }, - const p = category.get("params"); - if (p && Object.keys(p).length) { - if (p.order !== undefined) { - topicOpts.order = p.order; - } - if (p.ascending !== undefined) { - topicOpts.ascending = p.ascending; - } - } + setupController(controller, model) { + const topics = this.topics, + category = model.category, + canCreateTopic = topics.get("can_create_topic"); - this.controllerFor("discovery/topics").setProperties(topicOpts); - this.searchService.searchContext = category.get("searchContext"); - this.set("topics", null); - }, + let canCreateTopicOnCategory = + canCreateTopic && category.get("permission") === PermissionType.FULL; + let cannotCreateTopicOnCategory = !canCreateTopicOnCategory; + let defaultSubcategory; + let canCreateTopicOnSubCategory; - renderTemplate() { - this.render("navigation/category", { outlet: "navigation-bar" }); + if (this.siteSettings.default_subcategory_on_read_only_category) { + cannotCreateTopicOnCategory = false; - if (this._categoryList) { - this.render("discovery/categories", { - outlet: "header-list-container", - model: this._categoryList, + if (!canCreateTopicOnCategory && category.subcategories) { + defaultSubcategory = category.subcategories.find((subcategory) => { + return subcategory.get("permission") === PermissionType.FULL; }); - } else { - this.disconnectOutlet({ outlet: "header-list-container" }); + canCreateTopicOnSubCategory = !!defaultSubcategory; } - this.render("discovery/topics", { - controller: "discovery/topics", - outlet: "list-container", + } + + this.controllerFor("navigation/category").setProperties({ + canCreateTopicOnCategory, + cannotCreateTopicOnCategory, + canCreateTopic, + canCreateTopicOnSubCategory, + defaultSubcategory, + }); + + let topicOpts = { + model: topics, + category, + period: + topics.get("for_period") || + (model.modelParams && model.modelParams.period), + selected: [], + noSubcategories: this.routeConfig && !!this.routeConfig.no_subcategories, + expandAllPinned: true, + canCreateTopic, + canCreateTopicOnCategory, + canCreateTopicOnSubCategory, + defaultSubcategory, + }; + + const p = category.get("params"); + if (p && Object.keys(p).length) { + if (p.order !== undefined) { + topicOpts.order = p.order; + } + if (p.ascending !== undefined) { + topicOpts.ascending = p.ascending; + } + } + + this.controllerFor("discovery/topics").setProperties(topicOpts); + this.searchService.searchContext = category.get("searchContext"); + this.set("topics", null); + }, + + renderTemplate() { + this.render("navigation/category", { outlet: "navigation-bar" }); + + if (this._categoryList) { + this.render("discovery/categories", { + outlet: "header-list-container", + model: this._categoryList, }); - }, + } else { + this.disconnectOutlet({ outlet: "header-list-container" }); + } + this.render("discovery/topics", { + controller: "discovery/topics", + outlet: "list-container", + }); + }, - deactivate() { - this._super(...arguments); + deactivate() { + this._super(...arguments); - this.composer.set("prioritizedCategoryId", null); - this.searchService.searchContext = null; - }, + this.composer.set("prioritizedCategoryId", null); + this.searchService.searchContext = null; + }, - @action - setNotification(notification_level) { - this.currentModel.setNotification(notification_level); - }, + @action + setNotification(notification_level) { + this.currentModel.setNotification(notification_level); + }, - @action - triggerRefresh() { - this.refresh(); - }, + @action + triggerRefresh() { + this.refresh(); + }, - @action - changeSort(sortBy) { - changeSort.call(this, sortBy); - }, + @action + changeSort(sortBy) { + changeSort.call(this, sortBy); + }, - @action - resetParams(skipParams = []) { - resetParams.call(this, skipParams); - }, - }); -}; + @action + resetParams(skipParams = []) { + resetParams.call(this, skipParams); + }, +}); + +// A helper function to create a category route with parameters +export default function buildCategoryRoute(routeConfig) { + return AbstractCategoryRoute.extend({ routeConfig }); +}