mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Allow showing hashtag autocomplete results without term (#19219)
This commit allows us to type # in the UI and present autocomplete results immediately with the following logic for the topic composer, and reversed for the chat composer: * Categories the user can access and has not muted sorted by `topic_count` * Tags the user can access and has not muted sorted by `topic_count` * Chat channels the user is a member of sorted by `messages_count` So in effect, we allow searching for hashtags without a search term. To do this we add a new `search_without_term` to each data source so each one can define how it wants to handle this logic.
This commit is contained in:
38
spec/services/category_hashtag_data_source_spec.rb
Normal file
38
spec/services/category_hashtag_data_source_spec.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe CategoryHashtagDataSource do
|
||||
fab!(:category1) { Fabricate(:category, slug: "random", topic_count: 12) }
|
||||
fab!(:category2) { Fabricate(:category, slug: "books", topic_count: 566) }
|
||||
fab!(:category3) { Fabricate(:category, slug: "movies", topic_count: 245) }
|
||||
fab!(:group) { Fabricate(:group) }
|
||||
fab!(:category4) { Fabricate(:private_category, slug: "secret", group: group, topic_count: 40) }
|
||||
fab!(:category5) { Fabricate(:category, slug: "casual", topic_count: 99) }
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:guardian) { Guardian.new(user) }
|
||||
let(:uncategorized_category) { Category.find(SiteSetting.uncategorized_category_id) }
|
||||
|
||||
describe "#search_without_term" do
|
||||
it "returns distinct categories ordered by topic_count" do
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).to eq(
|
||||
["books", "movies", "casual", "random", "#{uncategorized_category.slug}"],
|
||||
)
|
||||
end
|
||||
|
||||
it "does not return categories the user does not have permission to view" do
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).not_to include("secret")
|
||||
group.add(user)
|
||||
expect(described_class.search_without_term(Guardian.new(user), 5).map(&:slug)).to include(
|
||||
"secret",
|
||||
)
|
||||
end
|
||||
|
||||
it "does not return categories the user has muted" do
|
||||
CategoryUser.create!(
|
||||
user: user,
|
||||
category: category1,
|
||||
notification_level: CategoryUser.notification_levels[:muted],
|
||||
)
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).not_to include("random")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,7 +3,8 @@
|
||||
RSpec.describe HashtagAutocompleteService do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:category1) { Fabricate(:category, name: "Book Club", slug: "book-club") }
|
||||
fab!(:tag1) { Fabricate(:tag, name: "great-books") }
|
||||
fab!(:tag1) { Fabricate(:tag, name: "great-books", topic_count: 22) }
|
||||
fab!(:topic1) { Fabricate(:topic) }
|
||||
let(:guardian) { Guardian.new(user) }
|
||||
|
||||
subject { described_class.new(guardian) }
|
||||
@@ -71,19 +72,19 @@ RSpec.describe HashtagAutocompleteService do
|
||||
describe "#search" do
|
||||
it "returns search results for tags and categories by default" do
|
||||
expect(subject.search("book", %w[category tag]).map(&:text)).to eq(
|
||||
["Book Club", "great-books x 0"],
|
||||
["Book Club", "great-books x 22"],
|
||||
)
|
||||
end
|
||||
|
||||
it "respects the types_in_priority_order param" do
|
||||
expect(subject.search("book", %w[tag category]).map(&:text)).to eq(
|
||||
["great-books x 0", "Book Club"],
|
||||
["great-books x 22", "Book Club"],
|
||||
)
|
||||
end
|
||||
|
||||
it "respects the limit param" do
|
||||
expect(subject.search("book", %w[tag category], limit: 1).map(&:text)).to eq(
|
||||
["great-books x 0"],
|
||||
["great-books x 22"],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -111,10 +112,10 @@ RSpec.describe HashtagAutocompleteService do
|
||||
|
||||
it "does case-insensitive search" do
|
||||
expect(subject.search("book", %w[category tag]).map(&:text)).to eq(
|
||||
["Book Club", "great-books x 0"],
|
||||
["Book Club", "great-books x 22"],
|
||||
)
|
||||
expect(subject.search("bOOk", %w[category tag]).map(&:text)).to eq(
|
||||
["Book Club", "great-books x 0"],
|
||||
["Book Club", "great-books x 22"],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -125,7 +126,7 @@ RSpec.describe HashtagAutocompleteService do
|
||||
|
||||
it "does not include categories the user cannot access" do
|
||||
category1.update!(read_restricted: true)
|
||||
expect(subject.search("book", %w[tag category]).map(&:text)).to eq(["great-books x 0"])
|
||||
expect(subject.search("book", %w[tag category]).map(&:text)).to eq(["great-books x 22"])
|
||||
end
|
||||
|
||||
it "does not include tags the user cannot access" do
|
||||
@@ -141,7 +142,7 @@ RSpec.describe HashtagAutocompleteService do
|
||||
HashtagAutocompleteService.register_data_source("bookmark", BookmarkDataSource)
|
||||
|
||||
expect(subject.search("book", %w[category tag bookmark]).map(&:text)).to eq(
|
||||
["Book Club", "great-books x 0", "read review of this fantasy book"],
|
||||
["Book Club", "great-books x 22", "read review of this fantasy book"],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -235,6 +236,38 @@ RSpec.describe HashtagAutocompleteService do
|
||||
expect(subject.search("book", %w[category tag]).map(&:text)).to eq(["Book Club"])
|
||||
end
|
||||
end
|
||||
|
||||
context "when no term is provided (default results) triggered by a # with no characters in the UI" do
|
||||
fab!(:category2) do
|
||||
Fabricate(:category, name: "Book Zone", slug: "book-zone", topic_count: 546)
|
||||
end
|
||||
fab!(:category3) do
|
||||
Fabricate(:category, name: "Book Dome", slug: "book-dome", topic_count: 987)
|
||||
end
|
||||
fab!(:category4) { Fabricate(:category, name: "Bookworld", slug: "book", topic_count: 56) }
|
||||
fab!(:category5) { Fabricate(:category, name: "Media", slug: "media", topic_count: 446) }
|
||||
fab!(:tag2) { Fabricate(:tag, name: "mid-books", topic_count: 33) }
|
||||
fab!(:tag3) { Fabricate(:tag, name: "terrible-books", topic_count: 2) }
|
||||
fab!(:tag4) { Fabricate(:tag, name: "book", topic_count: 1) }
|
||||
|
||||
it "returns the 'most polular' categories and tags (based on topic_count) that the user can access" do
|
||||
category1.update!(read_restricted: true)
|
||||
Fabricate(:tag_group, permissions: { "staff" => 1 }, tag_names: ["terrible-books"])
|
||||
|
||||
expect(subject.search(nil, %w[category tag]).map(&:text)).to eq(
|
||||
[
|
||||
"Book Dome",
|
||||
"Book Zone",
|
||||
"Media",
|
||||
"Bookworld",
|
||||
Category.find(SiteSetting.uncategorized_category_id).name,
|
||||
"mid-books x 33",
|
||||
"great-books x 22",
|
||||
"book x 1",
|
||||
],
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#lookup_old" do
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe TagHashtagDataSource do
|
||||
fab!(:tag1) { Fabricate(:tag, name: "fact") }
|
||||
fab!(:tag1) { Fabricate(:tag, name: "fact", topic_count: 0) }
|
||||
fab!(:tag2) { Fabricate(:tag, name: "factor", topic_count: 5) }
|
||||
fab!(:tag3) { Fabricate(:tag, name: "factory", topic_count: 1) }
|
||||
fab!(:tag4) { Fabricate(:tag, name: "factorio") }
|
||||
fab!(:tag5) { Fabricate(:tag, name: "factz") }
|
||||
fab!(:tag3) { Fabricate(:tag, name: "factory", topic_count: 4) }
|
||||
fab!(:tag4) { Fabricate(:tag, name: "factorio", topic_count: 3) }
|
||||
fab!(:tag5) { Fabricate(:tag, name: "factz", topic_count: 1) }
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:guardian) { Guardian.new(user) }
|
||||
|
||||
@@ -33,7 +33,7 @@ RSpec.describe TagHashtagDataSource do
|
||||
|
||||
it "includes the topic count for the text of the tag" do
|
||||
expect(described_class.search(guardian, "fact", 5).map(&:text)).to eq(
|
||||
["fact x 0", "factor x 5", "factory x 1", "factorio x 0", "factz x 0"],
|
||||
["fact x 0", "factor x 5", "factory x 4", "factorio x 3", "factz x 1"],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -42,4 +42,22 @@ RSpec.describe TagHashtagDataSource do
|
||||
expect(described_class.search(guardian, "fact", 5)).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe "#search_without_term" do
|
||||
it "returns distinct tags sorted by topic_count" do
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).to eq(
|
||||
%w[factor factory factorio factz fact],
|
||||
)
|
||||
end
|
||||
|
||||
it "does not return tags the user does not have permission to view" do
|
||||
Fabricate(:tag_group, permissions: { "staff" => 1 }, tag_names: ["factor"])
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).not_to include("factor")
|
||||
end
|
||||
|
||||
it "does not return tags the user has muted" do
|
||||
TagUser.create(user: user, tag: tag2, notification_level: TagUser.notification_levels[:muted])
|
||||
expect(described_class.search_without_term(guardian, 5).map(&:slug)).not_to include("factor")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user