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`
This commit is contained in:
David Taylor 2023-08-02 17:39:11 +01:00
parent 5aaf5cbaf4
commit d5107d1aba
2 changed files with 217 additions and 215 deletions

View File

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

View File

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