mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
appstore: implement sorting/searching in wishlist
This commit is contained in:
parent
62f246fba4
commit
902b706183
4 changed files with 207 additions and 32 deletions
11
store/app.py
11
store/app.py
|
@ -130,7 +130,16 @@ def star_app(app_id, action):
|
|||
|
||||
@app.route('/wishlist')
|
||||
def browse_wishlist():
|
||||
return render_template("wishlist.html", locale=get_locale(), user=session.get('user', {}), wishlist=get_wishlist(), stars=get_stars())
|
||||
return render_template(
|
||||
"wishlist.html",
|
||||
init_sort=request.args.get("sort"),
|
||||
init_search=request.args.get("search"),
|
||||
init_starsonly=request.args.get("starsonly"),
|
||||
locale=get_locale(),
|
||||
user=session.get('user', {}),
|
||||
wishlist=get_wishlist(),
|
||||
stars=get_stars()
|
||||
)
|
||||
|
||||
|
||||
@app.route('/wishlist/add', methods=['GET', 'POST'])
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% block main %}
|
||||
<div class="max-w-screen-md mx-auto pt-5">
|
||||
|
||||
<span class="flex items-end mb-1">
|
||||
<span class="flex flex-row items-end mb-1">
|
||||
<img {% if infos['logo_hash'] %}
|
||||
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
|
||||
{% else %}
|
||||
|
@ -11,7 +11,7 @@
|
|||
loading="lazy"
|
||||
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
|
||||
/>
|
||||
<h2 class="pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h2>
|
||||
<h2 class="flex-auto basis-1/4 pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h2>
|
||||
{% if infos['category'] %}
|
||||
<span class="ml-2 mb-1 rounded-full px-2.5 py-0.5 text-[10px] border text-{{ catalog['categories'][infos['category']]['color'] }}-500 border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
|
||||
{{ catalog['categories'][infos['category']]['title']|localize|lower }}
|
||||
|
@ -19,21 +19,23 @@
|
|||
{% endif %}
|
||||
<div class="grow"></div>
|
||||
|
||||
{% if infos['level'] == 0 %}
|
||||
<div class="py-2.5 px-3">
|
||||
<i class="text-md fa fa-exclamation-circle text-red-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% elif infos['level']|int <= 4 %}
|
||||
<div class="py-2.5 px-3">
|
||||
<i class="text-md fa fa-exclamation-triangle text-orange-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% elif infos['level'] == 8 %}
|
||||
<div class="py-2.5 px-3">
|
||||
<i class="text-md fa fa-diamond text-teal-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="h-9.5">
|
||||
|
||||
{% if infos['level'] == 0 %}
|
||||
<div class="py-2 px-3">
|
||||
<i class="text-md fa fa-exclamation-circle text-red-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% elif infos['level']|int <= 4 %}
|
||||
<div class="py-2 px-3">
|
||||
<i class="text-md fa fa-exclamation-triangle text-orange-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% elif infos['level'] == 8 %}
|
||||
<div class="py-2 px-3">
|
||||
<i class="text-md fa fa-diamond text-teal-500" aria-hidden="true"></i>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% set this_app_stars = stars.get(app_id, {})|length %}
|
||||
{% if user %}
|
||||
{% set user_starred_this_app = user['id'] in stars.get(app_id, {}) %}
|
||||
|
@ -69,7 +71,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
|
||||
<a class="inline-block rounded-md border p-0 bg-gray-900 text-white " href="https://install-app.yunohost.org/?app={{ app_id }}">
|
||||
<a class="h-9.5 inline-block rounded-md border p-0 bg-gray-900 text-white " href="https://install-app.yunohost.org/?app={{ app_id }}">
|
||||
<span class="inline-block text-[11px] leading-3 text-center py-1.5 pl-2">Install<br/>with</span>
|
||||
<span class="inline-block pr-2 pt-1"><img src="{{ url_for('static', filename='horizontal-yunohost.svg') }}" /></span>
|
||||
</a>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
|
||||
{% endif %}
|
||||
loading="lazy"
|
||||
class="h-16 w-16 rounded-lg object-cover shadow-sm mt-1"
|
||||
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
|
@ -261,7 +261,7 @@
|
|||
var queryArgs = new URLSearchParams(window.location.search)
|
||||
if (search_query) { queryArgs.set("search", search_query) } else { queryArgs.delete("search"); };
|
||||
if (category) { queryArgs.set("category", category) } else { queryArgs.delete("category"); };
|
||||
if (sortBy) { queryArgs.set("sort", sortBy) } else { queryArgs.delete("sortBy"); };
|
||||
if (sortBy) { queryArgs.set("sort", sortBy) } else { queryArgs.delete("sort"); };
|
||||
if (starsOnly) { queryArgs.set("starsonly", true) } else { queryArgs.delete("starsonly"); };
|
||||
|
||||
let newUrl;
|
||||
|
|
|
@ -27,6 +27,31 @@
|
|||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||
{{ _("Suggest an app") }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row justify-center items-center pt-2 text-center text-sm">
|
||||
<div class="inline-block px-2">
|
||||
{{ _("Sort by") }}
|
||||
<select
|
||||
name="selectsort"
|
||||
id="selectsort"
|
||||
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
|
||||
>
|
||||
<option {% if not init_sort %}selected{% endif %} value="">{{ _("Alphabetical") }}</option>
|
||||
<option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inline-block flex items-center px-2">
|
||||
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
|
||||
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} />
|
||||
|
||||
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
|
||||
</span>
|
||||
|
||||
<span class="absolute inset-y-0 start-0 m-1 h-2 w-2 rounded-full bg-white transition-all peer-checked:start-3">
|
||||
</span>
|
||||
</label>
|
||||
{{ _("Show only apps you starred") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -48,9 +73,19 @@
|
|||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tbody id="wishlist" class="divide-y divide-gray-200">
|
||||
{% for app, infos in wishlist.items() %}
|
||||
<tr>
|
||||
{% set this_app_stars = stars.get(app, {})|length %}
|
||||
{% if user %}
|
||||
{% set user_starred_this_app = user['id'] in stars.get(app, {}) %}
|
||||
{% else %}
|
||||
{% set user_starred_this_app = False %}
|
||||
{% endif %}
|
||||
<tr class="search-entry"
|
||||
data-appid="{{ app }}"
|
||||
data-stars="{{ this_app_stars }}"
|
||||
data-starred="{{ user_starred_this_app }}"
|
||||
>
|
||||
<td class="px-4 py-2 font-bold text-gray-900 max-w-[10em]">
|
||||
{{ infos['name'] }}
|
||||
</td>
|
||||
|
@ -76,12 +111,7 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center max-w-[5em]">
|
||||
{% set this_app_stars = stars.get(app, {})|length %}
|
||||
{% if user %}
|
||||
{% set user_starred_this_app = user['id'] in stars.get(app, {}) %}
|
||||
{% else %}
|
||||
{% set user_starred_this_app = False %}
|
||||
{% endif %}
|
||||
|
||||
<a
|
||||
href="{{ url_for('star_app', app_id=app, action="unstar" if user_starred_this_app else "star") }}"
|
||||
class="inline-block group btn-sm border text-violet-600 border-violet-500 hover:bg-violet-500 hover:text-white"
|
||||
|
@ -103,5 +133,139 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="noResultFound" class="text-center pt-5 hidden">
|
||||
<p class="text-lg font-bold text-gray-900 mb-5">
|
||||
{{ _("No results found.") }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// A little delay
|
||||
let typingTimer;
|
||||
let typeInterval = 500; // Half a second
|
||||
let searchInput = document.getElementById('search');
|
||||
let selectSort = document.getElementById('selectsort');
|
||||
let toggleStarsonly = document.getElementById('starsonly');
|
||||
|
||||
function liveSearch() {
|
||||
// Locate the card elements
|
||||
let entries = document.querySelectorAll('.search-entry')
|
||||
// Locate the search input
|
||||
let search_query = searchInput.value.trim().toLowerCase();
|
||||
let selectedCategory = false;
|
||||
let starsOnly = toggleStarsonly.checked;
|
||||
let at_least_one_match = false;
|
||||
// Loop through the entries
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
// If the text is within the card and the text matches the search query
|
||||
if ((entries[i].textContent.toLowerCase().includes(search_query))
|
||||
&& (! selectedCategory || (entries[i].dataset.category == selectedCategory))
|
||||
&& (! starsOnly || (entries[i].dataset.starred == "True")))
|
||||
{
|
||||
// ...remove the `.is-hidden` class.
|
||||
entries[i].classList.remove("hidden");
|
||||
at_least_one_match = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, add the class.
|
||||
entries[i].classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
if (at_least_one_match === false)
|
||||
{
|
||||
document.getElementById('noResultFound').classList.remove("hidden");
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById('noResultFound').classList.add("hidden");
|
||||
}
|
||||
|
||||
updateQueryArgsInURL()
|
||||
}
|
||||
|
||||
function liveSort(container_to_sort) {
|
||||
let sortBy = selectSort.value.trim();
|
||||
var toSort = document.getElementById(container_to_sort).children;
|
||||
toSort = Array.prototype.slice.call(toSort, 0);
|
||||
console.log(sortBy);
|
||||
if (sortBy === "newest") {
|
||||
toSort.sort(function(a, b) {
|
||||
return a.dataset.addedincatalog - b.dataset.addedincatalog;
|
||||
});
|
||||
}
|
||||
else if (sortBy === "popularity") {
|
||||
toSort.sort(function(a, b) {
|
||||
return a.dataset.stars < b.dataset.stars;
|
||||
});
|
||||
}
|
||||
else if (sortBy === "") {
|
||||
toSort.sort(function(a, b) {
|
||||
return a.dataset.appid > b.dataset.appid;
|
||||
});
|
||||
}
|
||||
var parent = document.getElementById(container_to_sort);
|
||||
parent.innerHTML = "";
|
||||
|
||||
for(var i = 0, l = toSort.length; i < l; i++) {
|
||||
parent.appendChild(toSort[i]);
|
||||
}
|
||||
console.log("gni?")
|
||||
updateQueryArgsInURL()
|
||||
}
|
||||
|
||||
function updateQueryArgsInURL() {
|
||||
let search_query = searchInput.value.trim();
|
||||
let category = false;
|
||||
let sortBy = selectSort.value.trim();
|
||||
let starsOnly = toggleStarsonly.checked;
|
||||
|
||||
if ('URLSearchParams' in window) {
|
||||
var queryArgs = new URLSearchParams(window.location.search)
|
||||
if (search_query) { queryArgs.set("search", search_query) } else { queryArgs.delete("search"); };
|
||||
if (category) { queryArgs.set("category", category) } else { queryArgs.delete("category"); };
|
||||
if (sortBy) { queryArgs.set("sort", sortBy) } else { queryArgs.delete("sort"); };
|
||||
if (starsOnly) { queryArgs.set("starsonly", true) } else { queryArgs.delete("starsonly"); };
|
||||
|
||||
console.log(queryArgs.toString());
|
||||
|
||||
let newUrl;
|
||||
if (queryArgs.toString())
|
||||
{
|
||||
newUrl = window.location.pathname + '?' + queryArgs.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
newUrl = window.location.pathname;
|
||||
}
|
||||
|
||||
if (newUrl != window.location.pathname + window.location.search)
|
||||
{
|
||||
history.pushState(null, '', newUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchInput.addEventListener('keyup', () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(liveSearch, typeInterval);
|
||||
});
|
||||
|
||||
selectSort.addEventListener('change', () => {
|
||||
liveSort("wishlist");
|
||||
});
|
||||
|
||||
toggleStarsonly.addEventListener('change', () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(liveSearch, typeInterval);
|
||||
});
|
||||
|
||||
liveSearch();
|
||||
liveSort("wishlist");
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
Loading…
Add table
Reference in a new issue