yunodevtools/store/templates/wishlist.html
2023-09-18 17:09:45 +02:00

272 lines
11 KiB
HTML

{% extends "base.html" %}
{% block main %}
<div class="text-center max-w-screen-md mx-auto mt-5">
<h2 class="text-2xl font-bold text-gray-900">
{{ _("Application Wishlist") }}
</h2>
<p class="text-sm text-gray-700 mx-3 mt-2">{{ _("The wishlist is the place where people can collectively suggest and vote for apps that they would like to see packaged and made available in YunoHost's official apps catalog. The fact that apps are listed here should by no mean be interpreted as a fact that YunoHost plans to integrate it, and is merely a source of inspiration for packaging volunteers.") }}</p>
</div>
<div class="max-w-screen-md mx-auto mt-3 mb-3">
<div class="flex flex-row">
<div class="px-2 inline-block relative basis-2/3 text-gray-700">
<label for="search" class="sr-only"> {{ _("Search") }} </label>
<input
type="text"
id="search"
placeholder="{{ _('Search for...') }}"
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
/>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
</div>
<a class="btn btn-primary-outline" href="{{ url_for('add_to_wishlist') }}">
<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 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}"{% endif %}>
<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 %} {% if not user%}disabled{% 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>
</div>
<div class="overflow-x-auto max-w-screen-lg mx-auto pt-5">
<table class="min-w-full divide-y-2 divide-gray-200 bg-white text-sm">
<thead>
<tr>
<th class="whitespace-nowrap px-4 py-2 font-medium text-gray-900">
{{ _("Name") }}
</th>
<th class="whitespace-nowrap px-4 py-2 font-medium text-gray-900">
{{ _("Description") }}
</th>
<th class="py-2"></th>
<th class="py-2"></th>
<th class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 max-w-[5em]">{{ _("Popularity") }}</th>
</tr>
</thead>
<tbody id="wishlist" class="divide-y divide-gray-200">
{% for app, infos in wishlist.items() %}
{% 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>
<td class="px-4 py-2 text-gray-700 max-w-md">{{ infos['description'] }}</td>
<td class="py-2">
{% if infos['website'] %}
<a
href="{{ infos['website'] }}"
class="inline-block"
>
<i class="fa fa-globe rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
<td class="py-2">
{% if infos['upstream'] %}
<a
href="{{ infos['upstream'] }}"
class="inline-block"
>
<i class="fa fa-code rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
<td class="text-center max-w-[5em]">
<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"
>
{% if not user_starred_this_app %}
<span class="inline-block {% if user %}group-hover:hidden{% endif %}">{{ this_app_stars }}</span>
<span class="hidden {% if user %}group-hover:inline-block{% endif %}">{{ this_app_stars+1 }}</span>
<i class="fa fa-star-o inline-block {% if user %}group-hover:hidden{% endif %}" aria-hidden="true"></i>
<i class="fa fa-star hidden {% if user %}group-hover:inline-block{% endif %}" aria-hidden="true"></i>
{% else %}
<span class="inline-block group-hover:hidden">{{ this_app_stars }}</span>
<span class="hidden group-hover:inline-block">{{ this_app_stars-1 }}</span>
<i class="fa fa-star inline-block group-hover:hidden" aria-hidden="true"></i>
<i class="fa fa-star-o hidden group-hover:inline-block" aria-hidden="true"></i>
{% endif %}
</a>
</td>
</tr>
{% 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 %}