Add sort option to bookmark list (#522)

* Rename BookmarkFilters to BookmarkSearch

* Refactor queries to accept BookmarkSearch

* Sort query by data added and title

* Ensure pagination respects search parameters

* Ensure tag cloud respects search parameters

* Ensure user select respects search parameters

* Ensure return url respects search options

* Fix passing search options to user select

* Fix BookmarkSearch initialization

* Extract common search form logic

* Ensure partial update respects search options

* Add sort UI

* Use custom ICU collation when sorting with SQLite

* Support sort in API
This commit is contained in:
Sascha Ißbrücker
2023-09-01 22:48:21 +02:00
committed by GitHub
parent 0c50906056
commit 0975914a86
35 changed files with 1026 additions and 361 deletions

View File

@@ -15,13 +15,14 @@
<div class="content-area-header mb-0">
<h2>Archived bookmarks</h2>
<div class="d-flex">
{% bookmark_search bookmark_list.filters tag_cloud.tags mode='archived' %}
{% bookmark_search bookmark_list.search tag_cloud.tags mode='archived' %}
{% include 'bookmarks/bulk_edit/toggle.html' %}
</div>
</div>
<form class="bookmark-actions" action="{% url 'bookmarks:archived.action' %}?q={{ bookmark_list.filters.query }}&return_url={{ bookmark_list.return_url }}"
method="post">
<form class="bookmark-actions"
action="{{ bookmark_list.action_url|safe }}"
method="post" autocomplete="off">
{% csrf_token %}
{% include 'bookmarks/bulk_edit/bar.html' with disable_actions='bulk_archive' %}

View File

@@ -65,7 +65,7 @@
{% endif %}
{% if bookmark_item.is_editable %}
{# Bookmark owner actions #}
<a href="{% url 'bookmarks:edit' bookmark_item.id %}?return_url={{ bookmark_list.return_url }}">Edit</a>
<a href="{% url 'bookmarks:edit' bookmark_item.id %}?return_url={{ bookmark_list.return_url|urlencode }}">Edit</a>
{% if bookmark_item.is_archived %}
<button type="submit" name="unarchive" value="{{ bookmark_item.id }}"
class="btn btn-link btn-sm">Unarchive

View File

@@ -15,12 +15,13 @@
<div class="content-area-header mb-0">
<h2>Bookmarks</h2>
<div class="d-flex">
{% bookmark_search bookmark_list.filters tag_cloud.tags %}
{% bookmark_search bookmark_list.search tag_cloud.tags %}
{% include 'bookmarks/bulk_edit/toggle.html' %}
</div>
</div>
<form class="bookmark-actions" action="{% url 'bookmarks:index.action' %}?q={{ bookmark_list.filters.query }}&return_url={{ bookmark_list.return_url }}"
<form class="bookmark-actions"
action="{{ bookmark_list.action_url|safe }}"
method="post" autocomplete="off">
{% csrf_token %}
{% include 'bookmarks/bulk_edit/bar.html' with disable_actions='bulk_unarchive' %}

View File

@@ -1,15 +1,46 @@
{% load widget_tweaks %}
<div class="search">
<form action="" method="get" role="search">
<div class="input-group">
<span id="search-input-wrap">
<input type="search" class="form-input" name="q" placeholder="Search for words or #tags"
value="{{ filters.query }}">
</span>
<input type="submit" value="Search" class="btn input-group-btn">
<div class="d-flex">
<div class="input-group">
<span id="search-input-wrap">
<input type="search" class="form-input" name="q" placeholder="Search for words or #tags"
value="{{ search.query }}">
</span>
<input type="submit" value="Search" class="btn input-group-btn">
</div>
<div class="search-options dropdown dropdown-right">
<button type="button" class="btn dropdown-toggle">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M4 10a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"></path>
<path d="M6 4v4"></path>
<path d="M6 12v8"></path>
<path d="M10 16a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"></path>
<path d="M12 4v10"></path>
<path d="M12 18v2"></path>
<path d="M16 7a2 2 0 1 0 4 0a2 2 0 0 0 -4 0"></path>
<path d="M18 4v1"></path>
<path d="M18 9v11"></path>
</svg>
</button>
<div class="menu text-sm" tabindex="0">
<div class="form-group">
<label for="{{ form.sort.id_for_label }}" class="form-label">Sort by</label>
{{ form.sort|add_class:"form-select select-sm" }}
</div>
<div class="actions">
<button type="submit" class="btn btn-sm btn-primary">Apply</button>
</div>
</div>
</div>
</div>
{% if filters.user %}
<input type="hidden" name="user" value="{{ filters.user }}">
{% endif %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
</form>
</div>
@@ -19,9 +50,9 @@
const currentTagsString = '{{ tags_string }}';
const currentTags = currentTagsString.split(' ');
const uniqueTags = [...new Set(currentTags)]
const filters = {
q: '{{ filters.query }}',
user: '{{ filters.user }}',
const search = {
q: '{{ search.query }}',
user: '{{ search.user }}',
}
const apiClient = new linkding.ApiClient('{% url 'bookmarks:api-root' %}')
const wrapper = document.getElementById('search-input-wrap')
@@ -31,12 +62,12 @@
props: {
name: 'q',
placeholder: 'Search for words or #tags',
value: '{{ filters.query }}',
value: '{{ search.query|safe }}',
tags: uniqueTags,
mode: '{{ mode }}',
linkTarget: '{{ request.user_profile.bookmark_link_target }}',
apiClient,
filters,
search,
}
})
wrapper.parentElement.replaceChild(newWrapper, wrapper)

View File

@@ -13,10 +13,10 @@
<section class="content-area col-2">
<div class="content-area-header">
<h2>Shared bookmarks</h2>
{% bookmark_search bookmark_list.filters tag_cloud.tags mode='shared' %}
{% bookmark_search bookmark_list.search tag_cloud.tags mode='shared' %}
</div>
<form class="bookmark-actions" action="{% url 'bookmarks:shared.action' %}?return_url={{ bookmark_list.return_url }}"
<form class="bookmark-actions" action="{{ bookmark_list.action_url|safe }}"
method="post">
{% csrf_token %}
@@ -32,7 +32,7 @@
<h2>User</h2>
</div>
<div>
{% user_select filters users %}
{% user_select bookmark_list.search users %}
<br>
</div>
<div class="content-area-header">

View File

@@ -1,19 +1,12 @@
{% load widget_tweaks %}
<form id="user-select" action="" method="get">
{% if filters.query %}
<input type="hidden" name="q" value="{{ filters.query }}">
{% endif %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
<div class="form-group">
<div class="d-flex">
<select name="user" class="form-select">
<option value="">Everyone</option>
{% for user in users %}
<option value="{{ user.username }}"
{% if user.username == filters.user %}selected{% endif %}
data-is-user-option>
{{ user.username }}
</option>
{% endfor %}
</select>
{{ form.user|add_class:"form-select" }}
<noscript>
<button type="submit" class="btn btn-link ml-2">Apply</button>
</noscript>