1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00
apps/store/templates/wishlist.html
2024-05-12 17:37:37 +02:00

282 lines
12 KiB
HTML

{% extends "base.html" %}
{% block title %}
{{ _("Application Wishlist") }}
{% endblock %}
{% block main %}
<div class="text-center max-w-screen-md mx-auto mt-5 mx-2">
<h1 class="text-2xl font-bold">
{{ _("Application Wishlist") }}
</h1>
<p class="text-sm opacity-75 mx-10 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. Nevertheless, the fact that apps are listed here should by no mean be interpreted as a fact that the YunoHost project 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-col md:flex-row items-center">
<div class="px-2 inline-block relative basis-2/3">
<label for="search" class="sr-only"> {{ _("Search") }} </label>
<input
type="text"
id="search"
placeholder="{{ _('Search for…') }}"
class="w-full sm:text-sm py-2 pe-10"
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4 opacity-75">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
</div>
<div class="pb-1 mb-2 mt-3 md:my-0">
<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>
<div class="flex flex-col md:flex-row justify-center items-center pt-1 text-center text-sm">
<div class="inline-block px-2">
{{ _("Sort by") }}
<select
name="selectsort"
id="selectsort"
class="inline-block text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if request.args.get("sort") in [None, "popularity"] %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</option>
</select>
</div>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}opacity-75{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("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-100 dark:divide-gray-700 text-sm">
<thead>
<tr>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium">
{{ _("Name") }}
</th>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium">
{{ _("Description") }}
</th>
<th class="hidden sm:table-cell py-2"></th>
<th class="hidden sm:table-cell py-2"></th>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium max-w-[5em]">{{ _("Popularity") }}</th>
</tr>
</thead>
<tbody id="wishlist" class="divide-y divide-gray-100 dark:divide-gray-700">
{% 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="inline-block sm:table-cell px-4 py-2 font-bold sm:max-w-[10em]">
{{ infos['name'] }}
</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 opacity-75 max-w-md">{{ infos['description'] }}</td>
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0">
{% if infos['website'] %}
<a
title="{{ _('Official website') }}"
aria-label="{{ _('Official website') }}"
href="{{ infos['website'] }}"
class="inline-block"
target="_blank"
>
<i class="fa fa-globe rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0">
{% if infos['upstream'] %}
<a
title="{{ _('Code repository') }}"
aria-label="{{ _('Code repository') }}"
href="{{ infos['upstream'] }}"
class="inline-block"
target="_blank"
>
<i class="fa fa-code rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0 text-center max-w-[5em]">
<a
role="button"
title="{{ _('Star this app') }}"
aria-label="{{ _('Star this app') }}"
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 mb-5">
{{ _("No results found.") }}
</p>
</div>
</div>
<script type="text/javascript">
// 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);
if (sortBy === "newest") {
toSort.sort(function(a, b) {
return a.dataset.addedincatalog - b.dataset.addedincatalog ? 1 : -1;
});
}
else if (sortBy === "popularity") {
toSort.sort(function(a, b) {
return parseInt(a.dataset.stars) < parseInt(b.dataset.stars) ? 1 : -1;
});
}
else if (sortBy === "alpha") {
toSort.sort(function(a, b) {
return a.dataset.appid > b.dataset.appid ? 1 : -1;
});
}
var parent = document.getElementById(container_to_sort);
parent.innerHTML = "";
for(var i = 0, l = toSort.length; i < l; i++) {
parent.appendChild(toSort[i]);
}
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 != "popularity") { queryArgs.set("sort", sortBy) } else { queryArgs.delete("sort"); };
if (starsOnly) { queryArgs.set("starsonly", true) } else { queryArgs.delete("starsonly"); };
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 %}