DEV: Introduce dedicated controller and route for `discovery.filter` (#20837)

Instead of being tied to the old implementation and constraints, a
dedicated route and controller for the `discovery.filter` app route will
allow us to iterate on changes much faster.
This commit is contained in:
Alan Guo Xiang Tan 2023-03-27 10:08:11 +08:00 committed by GitHub
parent 9c36a49668
commit e0155b6955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 133 additions and 75 deletions

View File

@ -1,33 +1,19 @@
{{#if this.isQueryFilterMode}}
<div class="topic-query-filter">
<div class="topic-query-filter__input">
{{d-icon "filter" class="topic-query-filter__icon"}}
<Input
class="topic-query-filter__filter-term"
@value={{this.queryString}}
@enter={{action @updateTopicsListQueryParams this.queryString}}
@type="text"
/>
</div>
</div>
{{else}}
<BreadCrumbs
@categories={{this.categories}}
@category={{this.category}}
@noSubcategories={{this.noSubcategories}}
@tag={{this.tag}}
@additionalTags={{this.additionalTags}}
/>
<BreadCrumbs
@categories={{this.categories}}
@category={{this.category}}
@noSubcategories={{this.noSubcategories}}
@tag={{this.tag}}
@additionalTags={{this.additionalTags}}
/>
{{#unless this.additionalTags}}
{{! nav bar doesn't work with tag intersections }}
<NavigationBar
@navItems={{this.navItems}}
@filterMode={{this.filterMode}}
@category={{this.category}}
/>
{{/unless}}
{{/if}}
{{#unless this.additionalTags}}
{{! nav bar doesn't work with tag intersections }}
<NavigationBar
@navItems={{this.navItems}}
@filterMode={{this.filterMode}}
@category={{this.category}}
/>
{{/unless}}
<div class="navigation-controls">
{{#if (and this.notCategoriesRoute this.site.mobileView this.canBulk)}}

View File

@ -6,18 +6,11 @@ import { NotificationLevels } from "discourse/lib/notification-levels";
import { getOwner } from "discourse-common/lib/get-owner";
import { htmlSafe } from "@ember/template";
import { inject as service } from "@ember/service";
import { equal } from "@ember/object/computed";
export default Component.extend(FilterModeMixin, {
router: service(),
dialog: service(),
tagName: "",
queryString: "",
init() {
this._super(...arguments);
this.queryString = this.filterQueryString;
},
// Should be a `readOnly` instead but some themes/plugins still pass
// the `categories` property into this component
@ -147,8 +140,6 @@ export default Component.extend(FilterModeMixin, {
return controller.canBulkSelect;
},
isQueryFilterMode: equal("filterMode", "filter"),
actions: {
changeCategoryNotificationLevel(notificationLevel) {
this.category.setNotification(notificationLevel);

View File

@ -0,0 +1,33 @@
import Controller from "@ember/controller";
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";
export default class extends Controller {
@tracked status = "";
queryParams = ["status"];
get queryString() {
let paramStrings = [];
this.queryParams.forEach((key) => {
if (this[key]) {
paramStrings.push(`${key}:${this[key]}`);
}
});
return paramStrings.join(" ");
}
@action
updateTopicsListQueryParams(queryString) {
for (const match of queryString.matchAll(/(\w+):([^:\s]+)/g)) {
const key = match[1];
const value = match[2];
if (this.queryParams.includes(key)) {
this.set(key, value);
}
}
}
}

View File

@ -1,6 +1,4 @@
import Controller, { inject as controller } from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import { action } from "@ember/object";
// Just add query params here to have them automatically passed to topic list filters.
export const queryParams = {
@ -29,31 +27,6 @@ export const queryParams = {
const controllerOpts = {
discoveryTopics: controller("discovery/topics"),
queryParams: Object.keys(queryParams),
@discourseComputed(...Object.keys(queryParams))
queryString() {
let paramStrings = [];
this.queryParams.forEach((key) => {
if (this[key]) {
paramStrings.push(`${key}:${this[key]}`);
}
});
return paramStrings.join(" ");
},
@action
updateTopicsListQueryParams(queryString) {
for (const match of queryString.matchAll(/(\w+):([^:\s]+)/g)) {
const key = match[1];
const value = match[2];
if (controllerOpts.queryParams.includes(key)) {
this.set(key, value);
}
}
},
};
// Default to `undefined`

View File

@ -6,7 +6,6 @@ import { TRACKED_QUERY_PARAM_VALUE } from "discourse/lib/topic-list-tracked-filt
export default Controller.extend(FilterModeMixin, {
discovery: controller(),
discoveryFilter: controller("discovery.filter"),
router: service(),
@discourseComputed("router.currentRoute.queryParams.f")

View File

@ -0,0 +1,12 @@
import Controller, { inject as controller } from "@ember/controller";
export default class extends Controller {
@controller("discovery/filter") discoveryFilter;
queryString = "";
constructor() {
super(...arguments);
this.queryString = this.discoveryFilter.queryString;
}
}

View File

@ -11,13 +11,6 @@ export default {
name: "dynamic-route-builders",
initialize(_container, app) {
app.register(
"controller:discovery.filter",
DiscoverySortableController.extend()
);
app.register("route:discovery.filter", buildTopicRoute("filter"));
app.register(
"controller:discovery.category",
DiscoverySortableController.extend()

View File

@ -0,0 +1,53 @@
import I18n from "I18n";
import DiscourseRoute from "discourse/routes/discourse";
import { isEmpty } from "@ember/utils";
import { action } from "@ember/object";
export default class extends DiscourseRoute {
queryParams = {
status: { replace: true, refreshModel: true },
};
model(data) {
return this.store.findFiltered("topicList", {
filter: "filter",
params: this.#filterQueryParams(data),
});
}
titleToken() {
const filterText = I18n.t("filters.filter.title");
return I18n.t("filters.with_topics", { filter: filterText });
}
setupController(_controller, model) {
this.controllerFor("discovery/topics").setProperties({ model });
}
renderTemplate() {
this.render("navigation/filter", { outlet: "navigation-bar" });
this.render("discovery/topics", {
controller: "discovery/topics",
outlet: "list-container",
});
}
// TODO(tgxworld): This action is required by the `discovery/topics` controller which is not necessary for this route.
// Figure out a way to remove this.
@action
changeSort() {}
#filterQueryParams(data) {
const params = {};
Object.keys(this.queryParams).forEach((key) => {
if (!isEmpty(data[key])) {
params[key] = data[key];
}
});
return params;
}
}

View File

@ -9,7 +9,5 @@
@hasDraft={{this.currentUser.has_topic_draft}}
@createTopic={{route-action "createTopic"}}
@skipCategoriesNavItem={{this.skipCategoriesNavItem}}
@filterQueryString={{this.discoveryFilter.queryString}}
@updateTopicsListQueryParams={{this.discoveryFilter.updateTopicsListQueryParams}}
/>
</DSection>

View File

@ -0,0 +1,20 @@
<DSection
@bodyClass="navigation-filter"
@class="navigation-container"
@scrollTop={{false}}
>
<div class="topic-query-filter">
<div class="topic-query-filter__input">
{{d-icon "filter" class="topic-query-filter__icon"}}
<Input
class="topic-query-filter__filter-term"
@value={{this.queryString}}
@enter={{action
this.discoveryFilter.updateTopicsListQueryParams
this.queryString
}}
@type="text"
/>
</div>
</div>
</DSection>