1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00

appstore: misc good practices / a11y tweaks

This commit is contained in:
Alexandre Aubin 2023-09-19 16:34:57 +02:00
parent 662fcd2af3
commit c6889e4b01
7 changed files with 82 additions and 58 deletions

View file

@ -1,18 +1,22 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}
{{ infos['manifest']['name'] }}
{% endblock %}
{% block main %} {% block main %}
<div class="max-w-screen-md mx-auto pt-5 px-5 lg:px-0"> <div class="max-w-screen-md mx-auto pt-5 px-5 lg:px-0">
<span class="sm:flex sm:flex-row sm:items-end mb-1"> <span class="sm:flex sm:flex-row sm:items-end mb-1">
<span class="flex flex-row items-end"> <span class="flex flex-row items-end">
<img {% if infos['logo_hash'] %} <img alt="{{ _('Logo for %(app)s',app=infos['manifest']['name']) }}"
{% if infos['logo_hash'] %}
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png" src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
{% else %} {% else %}
src="{{ url_for('static', filename='app_logo_placeholder.png') }}" src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %} {% endif %}
loading="lazy" loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1" class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
/> >
<h2 class="flex-0 pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h2> <h1 class="flex-0 pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h1>
{% if infos['category'] %} {% 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 "> <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 ">
@ -23,15 +27,15 @@
{% if infos['level'] == "?" or infos["level"]|int <= 4 %} {% if infos['level'] == "?" or infos["level"]|int <= 4 %}
<span class="ml-2 mb-1.5"> <span class="ml-2 mb-1.5">
<i class="fa fa-exclamation-circle text-red-500 py-0.5" <i class="fa fa-exclamation-circle text-red-500 py-0.5"
aria-hidden="true"
title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}" title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
aria-label="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
></i> ></i>
</span> </span>
{% elif infos['level'] == 8 %} {% elif infos['level'] == 8 %}
<span class="ml-2 mb-1.5"> <span class="ml-2 mb-1.5">
<i class="fa fa-diamond text-teal-500 py-0.5" <i class="fa fa-diamond text-teal-500 py-0.5"
aria-hidden="true"
title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}" title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
aria-label="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
></i> ></i>
</span> </span>
{% endif %} {% endif %}
@ -78,9 +82,9 @@
<span class="inline sm:hidden">{{ _("Demo") }}</span> <span class="inline sm:hidden">{{ _("Demo") }}</span>
</a> </a>
{% endif %} {% endif %}
<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 }}"> <a aria-label="{{ _('Install with YunoHost') }}" title="{{ _('Install with YunoHost') }}" 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 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> <span class="inline-block pr-2 pt-1"><img alt="YunoHost" src="{{ url_for('static', filename='horizontal-yunohost.svg') }}"></span>
</a> </a>
</div> </div>
@ -94,7 +98,7 @@
<div class="from-markdown">{{ infos["full_description_html"]|safe }}</div> <div class="from-markdown">{{ infos["full_description_html"]|safe }}</div>
{% if infos["screenshot"] %} {% if infos["screenshot"] %}
<img class="my-3" src="{{ infos["screenshot"] }}" /> <img alt="{{ _("Screenshot for %(app)s",app=infos["manifest"]["name"]) }}" class="my-3" src="{{ infos["screenshot"] }}">
{% endif %} {% endif %}
{% if infos["manifest"]["integration"]["architectures"] != "all" %} {% if infos["manifest"]["integration"]["architectures"] != "all" %}
@ -111,13 +115,13 @@
{% if infos["pre_install_html"] %} {% if infos["pre_install_html"] %}
<div class="my-3 rounded-md bg-blue-200 text-blue-800 px-5 py-2"> <div class="my-3 rounded-md bg-blue-200 text-blue-800 px-5 py-2">
<h3 class="inline-block text-xl mb-2 font-semibold">{{ _("Important infos before installing") }}</h3> <h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Important infos before installing") }}</h2>
<div class="from-markdown">{{ infos["pre_install_html"] | safe }}</div> <div class="from-markdown">{{ infos["pre_install_html"] | safe }}</div>
</div> </div>
{% endif %} {% endif %}
{% if infos["antifeatures"] %} {% if infos["antifeatures"] %}
<h3 class="inline-block text-xl mb-2 font-semibold">{{ _("Anti-features") }}</h3> <h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Anti-features") }}</h2>
<p class="inline-block text-sm">{{ _("(This app has features you may not like)") }}</p> <p class="inline-block text-sm">{{ _("(This app has features you may not like)") }}</p>
<div class="my-3 rounded-md bg-red-200 text-red-800 px-5 py-2"> <div class="my-3 rounded-md bg-red-200 text-red-800 px-5 py-2">
<ul> <ul>
@ -129,7 +133,7 @@
{% endif %} {% endif %}
<h3 class="text-xl mb-2 font-semibold">{{ _("Useful links") }}</h3> <h2 class="text-xl mb-2 font-semibold">{{ _("Useful links") }}</h2>
<div> <div>
{% set upstream = infos["manifest"]["upstream"] %} {% set upstream = infos["manifest"]["upstream"] %}
<a class="block btn btn-link my-1" href="https://spdx.org/licenses/{{upstream.license}}"><i class="fa fa-institution fa-fw" aria-hidden="true"></i> {{ _("License: %(license)s", license=upstream.license) }}</a> <a class="block btn btn-link my-1" href="https://spdx.org/licenses/{{upstream.license}}"><i class="fa fa-institution fa-fw" aria-hidden="true"></i> {{ _("License: %(license)s", license=upstream.license) }}</a>

View file

@ -2,11 +2,11 @@
<html lang="{{ locale }}"> <html lang="{{ locale }}">
<head> <head>
<title>{{ _("YunoHost app store") }}</title> <title>{{ _("YunoHost app store") }} | {% block title %}{% endblock %} </title>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{{ url_for('static', filename='tailwindcss.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='tailwindcss.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}" rel="preload"> <link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}">
<style type="text/tailwindcss"> <style type="text/tailwindcss">
@layer utilities { @layer utilities {
.btn { .btn {
@ -54,7 +54,7 @@
> >
<a class="block text-teal-600" href="/"> <a class="block text-teal-600" href="/">
<span class="sr-only">{{ _("Home") }}</span> <span class="sr-only">{{ _("Home") }}</span>
<img src="{{ url_for('static', filename='ynh_logo_roundcorner.png') }}" style="height: 3em;" /> <img alt="YunoHost Logo" src="{{ url_for('static', filename='ynh_logo_roundcorner.png') }}" style="height: 3em;">
</a> </a>
<div class="flex flex-1 items-center justify-end md:justify-between"> <div class="flex flex-1 items-center justify-end md:justify-between">
@ -99,10 +99,10 @@
class="group flex shrink-0 items-center rounded-md transition" class="group flex shrink-0 items-center rounded-md transition"
> >
<img <img
alt="Man" alt="Avatar"
src="{{ user['avatar_url'] }}" src="{{ user['avatar_url'] }}"
class="h-10 w-10 rounded-full object-cover" class="h-10 w-10 rounded-full object-cover"
/> >
<p class="ms-2 text-left text-xs inline-block"> <p class="ms-2 text-left text-xs inline-block">
<strong class="block font-medium">{{ user['username'] }}</strong> <strong class="block font-medium">{{ user['username'] }}</strong>
</p> </p>
@ -198,7 +198,7 @@
alt="Avatar" alt="Avatar"
src="{{ user['avatar_url'] }}" src="{{ user['avatar_url'] }}"
class="h-10 w-10 rounded-full object-cover inline-block" class="h-10 w-10 rounded-full object-cover inline-block"
/> >
<p class="ms-2 inline-block text-left"> <p class="ms-2 inline-block text-left">
<strong class="font-medium">{{ user['username'] }}</strong> <strong class="font-medium">{{ user['username'] }}</strong>
</p> </p>
@ -221,13 +221,15 @@
</div> </div>
</header> </header>
<main>
{% block main %} {% block main %}
{% endblock %} {% endblock %}
</main>
<footer class="h-5 mt-5"></footer> <footer class="h-5 mt-5"></footer>
</body> </body>
<script> <script type="text/javascript">
{% if user %} {% if user %}
document.getElementById('toggleUserMenu').addEventListener('click', () => { document.getElementById('toggleUserMenu').addEventListener('click', () => {
document.getElementById('userMenu').classList.toggle("hidden"); document.getElementById('userMenu').classList.toggle("hidden");

View file

@ -20,15 +20,15 @@
> >
<div class="flex justify-between gap-4"> <div class="flex justify-between gap-4">
<div class="shrink-0"> <div class="shrink-0">
<img <img alt="{{ _('Logo for %(app)s',app=infos['manifest']['name']) }}"
{% if infos['logo_hash'] %} {% if infos['logo_hash'] %}
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png" src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
{% else %} {% else %}
src="{{ url_for('static', filename='app_logo_placeholder.png') }}" src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %} {% endif %}
loading="lazy" loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1" class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
/> >
</div> </div>
<div class="w-full"> <div class="w-full">
<span class="flex"> <span class="flex">
@ -38,11 +38,12 @@
<span class="text-xs"> <span class="text-xs">
{% if infos['level'] == "?" or infos["level"]|int <= 4 %} {% if infos['level'] == "?" or infos["level"]|int <= 4 %}
<i class="fa fa-exclamation-circle text-red-500 py-0.5" <i class="fa fa-exclamation-circle text-red-500 py-0.5"
aria-hidden="true" aria-label="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}" title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
></i> ></i>
{% elif infos['level'] == 8 %} {% elif infos['level'] == 8 %}
<i class="fa fa-diamond text-teal-500 py-0.5" aria-hidden="true" <i class="fa fa-diamond text-teal-500 py-0.5"
aria-label="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}" title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
></i> ></i>
{% endif %} {% endif %}
@ -70,11 +71,14 @@
{%- endmacro %} {%- endmacro %}
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}
{{ _("Application Catalog") }}
{% endblock %}
{% block main %} {% block main %}
<div class="mt-5 text-center"> <div class="mt-5 text-center">
<h2 class="text-2xl font-bold text-gray-900"> <h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Catalog") }} {{ _("Application Catalog") }}
</h2> </h1>
</div> </div>
<div class="max-w-screen-md mx-auto mt-3 mb-3"> <div class="max-w-screen-md mx-auto mt-3 mb-3">
<div class="flex flex-col md:flex-row items-center"> <div class="flex flex-col md:flex-row items-center">
@ -87,7 +91,7 @@
placeholder="{{ _('Search for...') }}" placeholder="{{ _('Search for...') }}"
{% if init_search %}value="{{ init_search }}"{% endif %} {% if init_search %}value="{{ init_search }}"{% endif %}
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10" 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"> <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> <i class="fa fa-search" aria-hidden="true"></i>
@ -102,8 +106,7 @@
> >
<option value="">{{ _("All apps") }}</option> <option value="">{{ _("All apps") }}</option>
{% for id, category in catalog['categories'].items() %} {% for id, category in catalog['categories'].items() %}
{{ category['title']|localize }} <option {% if id == init_category %}selected{% endif %} value="{{ id }}">{{ category['title']|localize }}</option>
<option {% if id == init_category %}selected{% endif %} value="{{ id }}" {{ id == init_category }} >{{ category['title']|localize }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
@ -122,9 +125,10 @@
<option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option> <option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
</select> </select>
</div> </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') }}"{% endif %}> <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"> <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="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 class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span> </span>
@ -175,7 +179,7 @@
</div> </div>
<script> <script type="text/javascript">
// A little delay // A little delay
let typingTimer; let typingTimer;
let typeInterval = 500; // Half a second let typeInterval = 500; // Half a second

View file

@ -1,11 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}
{{ _("Home") }}
{% endblock %}
{% block main %} {% block main %}
<div class="mx-auto w-full text-center p-8"> <div class="mx-auto w-full text-center p-8">
<img src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto" /> <img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto">
<h2 class="text-2xl font-bold text-gray-900"> <h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Store") }} {{ _("Application Store") }}
</h2> </h1>
</div> </div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-5"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-5">
@ -14,9 +17,9 @@
href="{{ url_for('browse_catalog') }}" href="{{ url_for('browse_catalog') }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12" class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12"
> >
<h3 class="text-md font-bold text-gray-900"> <h2 class="text-md font-bold text-gray-900">
{{ _("Browse all applications") }} {{ _("Browse all applications") }}
</h3> </h2>
</a> </a>
</div> </div>
{% for id, category in catalog['categories'].items() %} {% for id, category in catalog['categories'].items() %}
@ -25,10 +28,10 @@
href="{{ url_for('browse_catalog', category=id) }}" href="{{ url_for('browse_catalog', category=id) }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-10" class="h-full relative block overflow-hidden hover:bg-gray-200 pt-10"
> >
<h3 class="text-md font-bold text-gray-900"> <h2 class="text-md font-bold text-gray-900">
<i class="fa fa-{{ category['icon'] }}" aria-hidden="true"></i> <i class="fa fa-{{ category['icon'] }}" aria-hidden="true"></i>
{{ category['title']|localize }} {{ category['title']|localize }}
</h3> </h2>
<p class="mx-auto max-w-[40ch] text-xs text-gray-500"> <p class="mx-auto max-w-[40ch] text-xs text-gray-500">
{{ category['description']|localize }} {{ category['description']|localize }}
</p> </p>

View file

@ -1,9 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}
{{ _("Application Wishlist") }}
{% endblock %}
{% block main %} {% block main %}
<div class="text-center max-w-screen-md mx-auto mt-5 mx-2"> <div class="text-center max-w-screen-md mx-auto mt-5 mx-2">
<h2 class="text-2xl font-bold text-gray-900"> <h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Wishlist") }} {{ _("Application Wishlist") }}
</h2> </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> <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>
@ -17,7 +20,7 @@
id="search" id="search"
placeholder="{{ _('Search for...') }}" placeholder="{{ _('Search for...') }}"
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10" 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"> <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> <i class="fa fa-search" aria-hidden="true"></i>
@ -43,9 +46,10 @@
<option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option> <option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
</select> </select>
</div> </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') }}"{% endif %}> <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"> <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="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 class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span> </span>
@ -96,6 +100,7 @@
{% if infos['website'] %} {% if infos['website'] %}
<a <a
title="{{ _('Official website') }}" title="{{ _('Official website') }}"
aria-label="{{ _('Official website') }}"
href="{{ infos['website'] }}" href="{{ infos['website'] }}"
class="inline-block" class="inline-block"
> >
@ -107,6 +112,7 @@
{% if infos['upstream'] %} {% if infos['upstream'] %}
<a <a
title="{{ _('Code repository') }}" title="{{ _('Code repository') }}"
aria-label="{{ _('Code repository') }}"
href="{{ infos['upstream'] }}" href="{{ infos['upstream'] }}"
class="inline-block" class="inline-block"
> >
@ -117,7 +123,9 @@
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0 text-center max-w-[5em]"> <td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0 text-center max-w-[5em]">
<a <a
role="button"
title="{{ _('Star this app') }}" 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") }}" 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" class="inline-block group btn-sm border text-violet-600 border-violet-500 hover:bg-violet-500 hover:text-white"
> >
@ -146,7 +154,7 @@
</div> </div>
</div> </div>
<script> <script type="text/javascript">
// A little delay // A little delay
let typingTimer; let typingTimer;
let typeInterval = 500; // Half a second let typeInterval = 500; // Half a second

View file

@ -1,9 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}
{{ _("Suggest an app") }}
{% endblock %}
{% block main %} {% block main %}
<div class="mt-5 text-center px-3 sm:px-0"> <div class="mt-5 text-center px-3 sm:px-0">
<h2 class="text-2xl font-bold text-gray-900"> <h1 class="text-2xl font-bold text-gray-900">
{{ _("Suggest an application to be added to YunoHost's catalog") }} {{ _("Suggest an application to be added to YunoHost's catalog") }}
</h2> </h1>
</div> </div>
@ -50,17 +53,17 @@
<form method="POST" action="{{ url_for('add_to_wishlist') }}" class="mt-8 mb-8" > <form method="POST" action="{{ url_for('add_to_wishlist') }}" class="mt-8 mb-8" >
<label for="name" class="mt-5 block font-bold text-gray-700">{{ _("Name") }}</label> <label for="name" class="mt-5 block font-bold text-gray-700">{{ _("Name") }}</label>
<input name="name" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" /> <input name="name" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" >
<label for="description" class="mt-5 block font-bold text-gray-700">{{ _("Description") }}</label> <label for="description" class="mt-5 block font-bold text-gray-700">{{ _("App's description") }}</label>
<textarea name="description" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" required rows='3' maxlength='100'></textarea> <textarea name="description" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" required rows='3' maxlength='100'></textarea>
<span class="text-xs text-gray-600"><span class="font-bold">{{ _("Please be concise and focus on what the app does.") }}</span> {{ _("No need to repeat '[App] is ...'. No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'.") }}</span> <span class="text-xs text-gray-600"><span class="font-bold">{{ _("Please be concise and focus on what the app does.") }}</span> {{ _("No need to repeat '[App] is ...'. No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'.") }}</span>
<label for="upstream" class="mt-5 block font-bold text-gray-700">{{ _("Project code repository") }}</label> <label for="upstream" class="mt-5 block font-bold text-gray-700">{{ _("Project code repository") }}</label>
<input name="upstream" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" required /> <input name="upstream" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" required >
<label for="website" class="mt-5 block font-bold text-gray-700">{{ _("Project website") }}</label> <label for="website" class="mt-5 block font-bold text-gray-700">{{ _("Project website") }}</label>
<input name="website" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" /> <input name="website" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" >
<span class="text-xs text-gray-600">{{ _("Please *do not* just copy-paste the code repository URL. If the project has no proper website, then leave the field empty.") }}</span> <span class="text-xs text-gray-600">{{ _("Please *do not* just copy-paste the code repository URL. If the project has no proper website, then leave the field empty.") }}</span>
<button <button

View file

@ -14,7 +14,7 @@ AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations")
def get_locale(): def get_locale():
# try to guess the language from the user accept # try to guess the language from the user accept
# The best match wins. # The best match wins.
return request.accept_languages.best_match(AVAILABLE_LANGUAGES) return request.accept_languages.best_match(AVAILABLE_LANGUAGES) or "en"
def get_catalog(): def get_catalog():