DEV: Remove experimental support for query string on /filter route (#20632)

This commit is contained in:
Alan Guo Xiang Tan 2023-03-22 10:04:57 +08:00 committed by GitHub
parent 520d4f504b
commit b06e31f8e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 92 additions and 100 deletions

View File

@ -3,11 +3,11 @@
<Input
class="topic-query-filter__input"
@value={{this.queryString}}
@enter={{route-action "changeQueryString" this.queryString}}
@enter={{action @updateTopicsListQueryParams this.queryString}}
/>
<DButton
@action={{route-action "changeQueryString" this.queryString}}
@action={{action @updateTopicsListQueryParams this.queryString}}
@icon="filter"
@class="btn-primary topic-query-filter__button"
@label="filters.filter.button.label"

View File

@ -6,12 +6,18 @@ 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 { alias, equal } from "@ember/object/computed";
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
@ -142,7 +148,6 @@ export default Component.extend(FilterModeMixin, {
},
isQueryFilterMode: equal("filterMode", "filter"),
queryString: alias("router.currentRoute.queryParams.q"),
actions: {
changeCategoryNotificationLevel(notificationLevel) {

View File

@ -1,4 +1,6 @@
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 = {
@ -27,6 +29,31 @@ 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`
@ -34,10 +61,6 @@ controllerOpts.queryParams.forEach((p) => {
controllerOpts[p] = queryParams[p].default;
});
export function changeQueryString(queryString) {
this.controller.set("q", queryString);
}
export function changeSort(sortBy) {
let model = this.controllerFor("discovery.topics").model;

View File

@ -6,6 +6,7 @@ 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

@ -10,17 +10,13 @@ export default {
after: "inject-discourse-objects",
name: "dynamic-route-builders",
initialize(container, app) {
const siteSettings = container.lookup("service:site-settings");
initialize(_container, app) {
app.register(
"controller:discovery.filter",
DiscoverySortableController.extend()
);
if (siteSettings.experimental_topics_filter) {
app.register(
"controller:discovery.filter",
DiscoverySortableController.extend()
);
app.register("route:discovery.filter", buildTopicRoute("filter"));
}
app.register("route:discovery.filter", buildTopicRoute("filter"));
app.register(
"controller:discovery.category",

View File

@ -1,5 +1,4 @@
import {
changeQueryString,
changeSort,
queryParams,
resetParams,
@ -168,11 +167,6 @@ export default function (filter, extras) {
changeSort.call(this, sortBy);
},
@action
changeQueryString(queryString) {
changeQueryString.call(this, queryString);
},
@action
resetParams(skipParams = []) {
resetParams.call(this, skipParams);

View File

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

View File

@ -848,17 +848,12 @@ class TopicQuery
end
if status = options[:status]
options[:q] ||= +""
options[:q] << " status:#{status}"
end
if options[:q].present?
result =
TopicsFilter.new(
scope: result,
guardian: @guardian,
category_id: options[:category],
).filter(options[:q])
).filter(status: options[:status])
end
if (filter = (options[:filter] || options[:f])) && @user

View File

@ -1,51 +1,31 @@
# frozen_string_literal: true
class TopicsFilter
def self.register_filter(matcher, &block)
self.filters[matcher] = block
end
def self.filters
@@filters ||= {}
end
register_filter(/\Astatus:([a-zA-Z]+)\z/i) do |topics, match|
case match
when "open"
topics.where("NOT topics.closed AND NOT topics.archived")
when "closed"
topics.where("topics.closed")
when "archived"
topics.where("topics.archived")
when "deleted"
if @guardian.can_see_deleted_topics?(@category)
topics.unscope(where: :deleted_at).where("topics.deleted_at IS NOT NULL")
end
end
end
def initialize(guardian:, scope: Topic, category_id: nil)
@guardian = guardian
@scope = scope
@category = category_id.present? ? Category.find_by(id: category_id) : nil
end
def filter(input)
input
.to_s
.scan(/(([^" \t\n\x0B\f\r]+)?(("[^"]+")?))/)
.to_a
.map do |(word, _)|
next if word.blank?
self.class.filters.each do |matcher, block|
cleaned = word.gsub(/["']/, "")
new_scope = instance_exec(@scope, $1, &block) if cleaned =~ matcher
@scope = new_scope if !new_scope.nil?
end
end
def filter(status: nil)
filter_status(@scope, status) if status
@scope
end
private
def filter_status(scope, status)
case status
when "open"
@scope = @scope.where("NOT topics.closed AND NOT topics.archived")
when "closed"
@scope = @scope.where("topics.closed")
when "archived"
@scope = @scope.where("topics.archived")
when "deleted"
if @guardian.can_see_deleted_topics?(@category)
@scope = @scope.unscope(where: :deleted_at).where("topics.deleted_at IS NOT NULL")
end
end
end
end

View File

@ -9,41 +9,37 @@ RSpec.describe TopicsFilter do
describe "#filter" do
it "should return all topics when input is blank" do
expect(TopicsFilter.new(guardian: Guardian.new).filter("").pluck(:id)).to contain_exactly(
expect(TopicsFilter.new(guardian: Guardian.new).filter.pluck(:id)).to contain_exactly(
topic.id,
closed_topic.id,
archived_topic.id,
)
end
it "should return all topics when input does not match any filters" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter("randomstring").pluck(:id),
).to contain_exactly(topic.id, closed_topic.id, archived_topic.id)
end
context "when filtering by topic's status" do
it "should only return topics that have not been closed or archived when input is `status:open`" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter(status: "open").pluck(:id),
).to contain_exactly(topic.id)
end
it "should only return topics that have not been closed or archived when input is `status:open`" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter("status:open").pluck(:id),
).to contain_exactly(topic.id)
end
it "should only return topics that have been deleted when input is `status:deleted` and user can see deleted topics" do
expect(
TopicsFilter.new(guardian: Guardian.new(admin)).filter(status: "deleted").pluck(:id),
).to contain_exactly(deleted_topic_id)
end
it "should only return topics that have been deleted when input is `status:deleted` and user can see deleted topics" do
expect(
TopicsFilter.new(guardian: Guardian.new(admin)).filter("status:deleted").pluck(:id),
).to contain_exactly(deleted_topic_id)
end
it "should status filter when input is `status:deleted` and user cannot see deleted topics" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter(status: "deleted").pluck(:id),
).to contain_exactly(topic.id, closed_topic.id, archived_topic.id)
end
it "should status filter when input is `status:deleted` and user cannot see deleted topics" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter("status:deleted").pluck(:id),
).to contain_exactly(topic.id, closed_topic.id, archived_topic.id)
end
it "should only return topics that have been archived when input is `status:archived`" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter("status:archived").pluck(:id),
).to contain_exactly(archived_topic.id)
it "should only return topics that have been archived when input is `status:archived`" do
expect(
TopicsFilter.new(guardian: Guardian.new).filter(status: "archived").pluck(:id),
).to contain_exactly(archived_topic.id)
end
end
end
end

View File

@ -8,7 +8,7 @@ describe "Filtering topics", type: :system, js: true do
before { SiteSetting.experimental_topics_filter = true }
it "should allow users to enter a custom query string to filter through topics" do
it "should allow users to input a custom query string to filter through topics" do
sign_in(user)
visit("/filter")
@ -21,19 +21,19 @@ describe "Filtering topics", type: :system, js: true do
expect(topic_list).to have_topic(topic)
expect(topic_list).to have_no_topic(closed_topic)
expect(page).to have_current_path("/filter?q=status%3Aopen")
expect(page).to have_current_path("/filter?status=open")
topic_query_filter.fill_in("status:closed")
expect(topic_list).to have_no_topic(topic)
expect(topic_list).to have_topic(closed_topic)
expect(page).to have_current_path("/filter?q=status%3Aclosed")
expect(page).to have_current_path("/filter?status=closed")
end
it "should filter topics when 'q' query params is present" do
it "should filter topics when 'status' query params is present" do
sign_in(user)
visit("/filter?q=status:open")
visit("/filter?status=open")
expect(topic_list).to have_topic(topic)
expect(topic_list).to have_no_topic(closed_topic)