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
2023-10-27 11:57:56 +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 text-gray-900">
{{ _("Application Wishlist") }}
</h1>
<p class="text-sm text-gray-700 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 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>
<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 rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if not init_sort or init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if init_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 %}text-gray-500{% 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 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="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium text-gray-900">
{{ _("Name") }}
</th>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium text-gray-900">
{{ _("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 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="inline-block sm:table-cell px-4 py-2 font-bold text-gray-900 sm:max-w-[10em]">
{{ infos['name'] }}
</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 text-gray-700 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 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 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 text-gray-900 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 %}