FEATURE: Add search to user bookmark list (#10230)
User bookmarks can now be searched by name or post raw content. The q querystring param is hooked up from the Ember router as well.
This commit is contained in:
parent
f4f3e8c401
commit
bcc80e0ea8
|
@ -14,6 +14,10 @@ export default Controller.extend({
|
||||||
content: null,
|
content: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
noResultsHelp: null,
|
noResultsHelp: null,
|
||||||
|
searchTerm: null,
|
||||||
|
q: null,
|
||||||
|
|
||||||
|
queryParams: ["q"],
|
||||||
|
|
||||||
loadItems() {
|
loadItems() {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -22,8 +26,12 @@ export default Controller.extend({
|
||||||
noResultsHelp: null
|
noResultsHelp: null
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.q && !this.searchTerm) {
|
||||||
|
this.set("searchTerm", this.q);
|
||||||
|
}
|
||||||
|
|
||||||
return this.model
|
return this.model
|
||||||
.loadItems()
|
.loadItems({ q: this.searchTerm })
|
||||||
.then(response => this._processLoadResponse(response))
|
.then(response => this._processLoadResponse(response))
|
||||||
.catch(() => this._bookmarksListDenied())
|
.catch(() => this._bookmarksListDenied())
|
||||||
.finally(() =>
|
.finally(() =>
|
||||||
|
@ -43,6 +51,12 @@ export default Controller.extend({
|
||||||
this.content.removeObject(bookmark);
|
this.content.removeObject(bookmark);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@action
|
||||||
|
search() {
|
||||||
|
this.set("q", this.searchTerm);
|
||||||
|
this.loadItems();
|
||||||
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
removeBookmark(bookmark) {
|
removeBookmark(bookmark) {
|
||||||
const deleteBookmark = () => {
|
const deleteBookmark = () => {
|
||||||
|
@ -86,7 +100,7 @@ export default Controller.extend({
|
||||||
this.set("loadingMore", true);
|
this.set("loadingMore", true);
|
||||||
|
|
||||||
return this.model
|
return this.model
|
||||||
.loadMore()
|
.loadMore({ q: this.searchTerm })
|
||||||
.then(response => this._processLoadResponse(response))
|
.then(response => this._processLoadResponse(response))
|
||||||
.catch(() => this._bookmarksListDenied())
|
.catch(() => this._bookmarksListDenied())
|
||||||
.finally(() => this.set("loadingMore", false));
|
.finally(() => this.set("loadingMore", false));
|
||||||
|
|
|
@ -120,11 +120,17 @@ const Bookmark = RestModel.extend({
|
||||||
).capitalize();
|
).capitalize();
|
||||||
},
|
},
|
||||||
|
|
||||||
loadItems() {
|
loadItems(params) {
|
||||||
return ajax(`/u/${this.user.username}/bookmarks.json`, { cache: "false" });
|
let url = `/u/${this.user.username}/bookmarks.json`;
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
url += "?" + $.param(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ajax(url, { cache: "false" });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadMore() {
|
loadMore(additionalParams) {
|
||||||
if (!this.more_bookmarks_url) {
|
if (!this.more_bookmarks_url) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -136,7 +142,15 @@ const Bookmark = RestModel.extend({
|
||||||
if (params) {
|
if (params) {
|
||||||
moreUrl += "?" + params;
|
moreUrl += "?" + params;
|
||||||
}
|
}
|
||||||
|
if (additionalParams) {
|
||||||
|
if (moreUrl.includes("?")) {
|
||||||
|
moreUrl += "&" + $.param(additionalParams);
|
||||||
|
} else {
|
||||||
|
moreUrl += "?" + $.param(additionalParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ajax({ url: moreUrl });
|
return ajax({ url: moreUrl });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
|
queryParams: {
|
||||||
|
q: { replace: true }
|
||||||
|
},
|
||||||
|
|
||||||
redirect() {
|
redirect() {
|
||||||
this.transitionTo("userActivity.bookmarks");
|
this.transitionTo("userActivity.bookmarks");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
<div class="form-horizontal" style="margin-bottom: 1em">
|
||||||
|
{{input type="text"
|
||||||
|
value=searchTerm
|
||||||
|
placeholder=(i18n "bookmarks.search_placeholder")
|
||||||
|
enter=(action "search")
|
||||||
|
id="bookmark-search" autocomplete="discourse"}}
|
||||||
|
{{d-button
|
||||||
|
class="btn-primary"
|
||||||
|
action=(action "search")
|
||||||
|
type="button"
|
||||||
|
label="bookmarks.search"}}
|
||||||
|
</div>
|
||||||
{{#if noContent}}
|
{{#if noContent}}
|
||||||
<div class="alert alert-info">{{noResultsHelp}}</div>
|
<div class="alert alert-info">{{noResultsHelp}}</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
@ -6,6 +6,6 @@ class UserBookmarkListSerializer < ApplicationSerializer
|
||||||
has_many :bookmarks, serializer: UserBookmarkSerializer, embed: :objects
|
has_many :bookmarks, serializer: UserBookmarkSerializer, embed: :objects
|
||||||
|
|
||||||
def include_more_bookmarks_url?
|
def include_more_bookmarks_url?
|
||||||
object.bookmarks.size == object.per_page
|
@include_more_bookmarks_url ||= object.bookmarks.size == object.per_page
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -320,6 +320,8 @@ en:
|
||||||
invalid_custom_datetime: "The date and time you provided is invalid, please try again."
|
invalid_custom_datetime: "The date and time you provided is invalid, please try again."
|
||||||
list_permission_denied: "You do not have permission to view this user's bookmarks."
|
list_permission_denied: "You do not have permission to view this user's bookmarks."
|
||||||
delete_when_reminder_sent: "Delete this bookmark when the reminder notification is sent."
|
delete_when_reminder_sent: "Delete this bookmark when the reminder notification is sent."
|
||||||
|
search_placeholder: "Search bookmarks by name or post content"
|
||||||
|
search: "Search"
|
||||||
reminders:
|
reminders:
|
||||||
at_desktop: "Next time I'm at my desktop"
|
at_desktop: "Next time I'm at my desktop"
|
||||||
later_today: "Later today"
|
later_today: "Later today"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddBookmarkNameIndex < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_index :bookmarks, :name
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,6 +35,10 @@ class BookmarkQuery
|
||||||
|
|
||||||
results = results.merge(Post.secured(@guardian))
|
results = results.merge(Post.secured(@guardian))
|
||||||
|
|
||||||
|
if @params[:q].present?
|
||||||
|
results = results.where("bookmarks.name ILIKE :q OR posts.raw ILIKE :q", q: "%#{@params[:q]}%")
|
||||||
|
end
|
||||||
|
|
||||||
if @page.positive?
|
if @page.positive?
|
||||||
results = results.offset(@page * @params[:per_page])
|
results = results.offset(@page * @params[:per_page])
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,8 +11,9 @@ RSpec.describe BookmarkQuery do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#list_all" do
|
describe "#list_all" do
|
||||||
fab!(:bookmark1) { Fabricate(:bookmark, user: user) }
|
fab!(:post) { Fabricate(:post, raw: "Some post content here") }
|
||||||
fab!(:bookmark2) { Fabricate(:bookmark, user: user) }
|
fab!(:bookmark1) { Fabricate(:bookmark, user: user, name: "Check up later") }
|
||||||
|
fab!(:bookmark2) { Fabricate(:bookmark, user: user, post: post, topic: post.topic) }
|
||||||
|
|
||||||
it "returns all the bookmarks for a user" do
|
it "returns all the bookmarks for a user" do
|
||||||
expect(bookmark_query.list_all.count).to eq(2)
|
expect(bookmark_query.list_all.count).to eq(2)
|
||||||
|
@ -37,6 +38,18 @@ RSpec.describe BookmarkQuery do
|
||||||
expect(preloaded_bookmarks.any?).to eq(true)
|
expect(preloaded_bookmarks.any?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when q param is provided" do
|
||||||
|
it "can search by post content" do
|
||||||
|
bookmarks = bookmark_query(params: { q: 'content' }).list_all
|
||||||
|
expect(bookmarks.map(&:id)).to eq([bookmark2.id])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can search by bookmark name" do
|
||||||
|
bookmarks = bookmark_query(params: { q: 'check' }).list_all
|
||||||
|
expect(bookmarks.map(&:id)).to eq([bookmark1.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "for a whispered post" do
|
context "for a whispered post" do
|
||||||
before do
|
before do
|
||||||
bookmark1.post.update(post_type: Post.types[:whisper])
|
bookmark1.post.update(post_type: Post.types[:whisper])
|
||||||
|
|
Loading…
Reference in New Issue