mirror of
https://github.com/YunoHost/yunodevtools.git
synced 2024-09-03 20:16:19 +02:00
appstore: draft app page with markdown description, screenshot, buttons for demo and vote
This commit is contained in:
parent
c6cfcfdc78
commit
6915740484
6 changed files with 131 additions and 11 deletions
44
store/app.py
44
store/app.py
|
@ -1,3 +1,4 @@
|
||||||
|
import markdown
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import toml
|
import toml
|
||||||
|
@ -13,6 +14,7 @@ from slugify import slugify
|
||||||
from flask import Flask, send_from_directory, render_template, session, redirect, request
|
from flask import Flask, send_from_directory, render_template, session, redirect, request
|
||||||
from github import Github, InputGitAuthor
|
from github import Github, InputGitAuthor
|
||||||
|
|
||||||
|
locale = "en"
|
||||||
app = Flask(__name__, static_url_path='/assets', static_folder="assets")
|
app = Flask(__name__, static_url_path='/assets', static_folder="assets")
|
||||||
catalog = json.load(open("apps.json"))
|
catalog = json.load(open("apps.json"))
|
||||||
catalog['categories'] = {c['id']:c for c in catalog['categories']}
|
catalog['categories'] = {c['id']:c for c in catalog['categories']}
|
||||||
|
@ -122,8 +124,48 @@ def browse_catalog(category_filter=None):
|
||||||
@app.route('/app/<app_id>')
|
@app.route('/app/<app_id>')
|
||||||
def app_info(app_id):
|
def app_info(app_id):
|
||||||
infos = catalog["apps"].get(app_id)
|
infos = catalog["apps"].get(app_id)
|
||||||
if not infos:
|
app_folder = os.path.join(config["APPS_CACHE"], app_id)
|
||||||
|
if not infos or not os.path.exists(app_folder):
|
||||||
return f"App {app_id} not found", 404
|
return f"App {app_id} not found", 404
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(app_folder, "doc", f"DESCRIPTION_{locale}.md")):
|
||||||
|
description_path = os.path.join(app_folder, "doc", f"DESCRIPTION_{locale}.md")
|
||||||
|
elif os.path.exists(os.path.join(app_folder, "doc", "DESCRIPTION.md")):
|
||||||
|
description_path = os.path.join(app_folder, "doc", "DESCRIPTION.md")
|
||||||
|
else:
|
||||||
|
description_path = None
|
||||||
|
if description_path:
|
||||||
|
with open(description_path) as f:
|
||||||
|
infos["full_description_html"] = markdown.markdown(f.read())
|
||||||
|
else:
|
||||||
|
infos["full_description_html"] = infos['manifest']['description'][locale]
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(app_folder, "doc", f"PRE_INSTALL_{locale}.md")):
|
||||||
|
pre_install_path = os.path.join(app_folder, "doc", f"PRE_INSTALL_{locale}.md")
|
||||||
|
elif os.path.exists(os.path.join(app_folder, "doc", "PRE_INSTALL.md")):
|
||||||
|
pre_install_path = os.path.join(app_folder, "doc", "PRE_INSTALL.md")
|
||||||
|
else:
|
||||||
|
pre_install_path = None
|
||||||
|
if pre_install_path:
|
||||||
|
with open(pre_install_path) as f:
|
||||||
|
infos["pre_install_html"] = markdown.markdown(f.read())
|
||||||
|
|
||||||
|
infos["screenshot"] = None
|
||||||
|
|
||||||
|
screenshots_folder = os.path.join(app_folder, "doc", "screenshots")
|
||||||
|
|
||||||
|
if os.path.exists(screenshots_folder):
|
||||||
|
with os.scandir(screenshots_folder) as it:
|
||||||
|
for entry in it:
|
||||||
|
ext = os.path.splitext(entry.name)[1].replace(".", "").lower()
|
||||||
|
if entry.is_file() and ext in ("png", "jpg", "jpeg", "webp", "gif"):
|
||||||
|
with open(entry.path, "rb") as img_file:
|
||||||
|
data = base64.b64encode(img_file.read()).decode("utf-8")
|
||||||
|
infos[
|
||||||
|
"screenshot"
|
||||||
|
] = f"data:image/{ext};charset=utf-8;base64,{data}"
|
||||||
|
break
|
||||||
|
|
||||||
return render_template("app.html", user=session.get('user', {}), app_id=app_id, infos=infos)
|
return render_template("app.html", user=session.get('user', {}), app_id=app_id, infos=infos)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,4 @@ Flask==2.3.2
|
||||||
python-slugify
|
python-slugify
|
||||||
PyGithub
|
PyGithub
|
||||||
toml
|
toml
|
||||||
|
markdown
|
||||||
|
|
|
@ -1,9 +1,55 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="max-w-screen-lg mx-auto pt-5">
|
<div class="max-w-screen-md mx-auto pt-5">
|
||||||
|
|
||||||
<h2>{{ app_id }}</h2>
|
<span class="flex mb-3">
|
||||||
|
<img {% if infos['logo_hash'] %}
|
||||||
|
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
|
||||||
|
{% else %}
|
||||||
|
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
|
||||||
|
{% endif %}
|
||||||
|
loading="lazy"
|
||||||
|
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
|
||||||
|
/>
|
||||||
|
<h2 class="grow pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h2>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="mr-3 inline-block group btn border text-violet-600 border-violet-500 hover:bg-violet-500 hover:text-white"
|
||||||
|
>
|
||||||
|
123 <i class="fa fa-star-o inline-block group-hover:hidden" aria-hidden="true"></i>
|
||||||
|
<i class="fa fa-star hidden group-hover:inline-block" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
|
{% if infos["manifest"]["upstream"]["demo"] %}
|
||||||
|
<a
|
||||||
|
class="btn btn-success inline-block"
|
||||||
|
href="{{ infos["manifest"]["upstream"]["demo"] }}"
|
||||||
|
>
|
||||||
|
<i class="fa fa-external-link fa-fw" aria-hidden="true"></i>
|
||||||
|
Try the demo
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
|
||||||
<p>{{ infos }}</p>
|
<p class="text-sm text-slate-500">Current version: {{ infos["manifest"]["version"] }}</p>
|
||||||
|
{% if infos["potential_alternative_to"] %}
|
||||||
|
<p class="text-sm text-slate-500">Potential alternative to: {{ infos["potential_alternative_to"]|join(', ') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="from-markdown">{{ infos["full_description_html"]|safe }}</div>
|
||||||
|
|
||||||
|
{% if infos["screenshot"] %}
|
||||||
|
<img src="{{ infos["screenshot"] }}" />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<p>{{ infos["category"] }}</p>
|
||||||
|
<p>{{ infos["level"] }}</p>
|
||||||
|
|
||||||
|
<p>Antifeatures: {{ infos["anti_features"] }}</p>
|
||||||
|
<p>{{ infos["manifest"]["integration"] }}</p>
|
||||||
|
<p>{{ infos["manifest"]["upstream"] }}</p>
|
||||||
|
<div class="from-markdown">{{ infos["pre_install_html"] | safe }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,6 +7,38 @@
|
||||||
<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 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') }}" rel="preload">
|
||||||
|
<style type="text/tailwindcss">
|
||||||
|
@layer utilities {
|
||||||
|
.btn {
|
||||||
|
@apply text-sm font-medium rounded-md px-4 py-2.5 transition;
|
||||||
|
}
|
||||||
|
.btn-sm {
|
||||||
|
@apply text-xs font-medium rounded-md px-2 py-2 transition;
|
||||||
|
}
|
||||||
|
.btn-success {
|
||||||
|
@apply text-white bg-green-500 hover:bg-green-700;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
@apply text-white bg-blue-500 hover:bg-blue-700;
|
||||||
|
}
|
||||||
|
.btn-primary-outline {
|
||||||
|
@apply border text-blue-600 border-blue-500 hover:text-blue-400;
|
||||||
|
}
|
||||||
|
.from-markdown {
|
||||||
|
@apply my-4;
|
||||||
|
}
|
||||||
|
.from-markdown p {
|
||||||
|
@apply mb-2;
|
||||||
|
}
|
||||||
|
.from-markdown h3 {
|
||||||
|
@apply text-xl mb-1 font-semibold;
|
||||||
|
}
|
||||||
|
.from-markdown ul {
|
||||||
|
padding: revert;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -39,7 +71,7 @@
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<div class="sm:flex sm:gap-4">
|
<div class="sm:flex sm:gap-4">
|
||||||
<a
|
<a
|
||||||
class="rounded-md border text-blue-600 border-blue-500 px-5 py-2.5 text-sm font-medium hover:text-blue-400 hidden md:block"
|
class="btn btn-primary-outline hidden md:inline-block"
|
||||||
href="https://yunohost.org/docs/"
|
href="https://yunohost.org/docs/"
|
||||||
>
|
>
|
||||||
<i class="fa fa-external-link fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-external-link fa-fw" aria-hidden="true"></i>
|
||||||
|
@ -47,7 +79,7 @@
|
||||||
</a>
|
</a>
|
||||||
{% if not user %}
|
{% if not user %}
|
||||||
<a
|
<a
|
||||||
class="rounded-md bg-blue-500 px-5 py-2.5 text-sm font-medium text-white transition hover:bg-blue-700 hidden md:block"
|
class="btn btn-primary hidden md:inline-block"
|
||||||
href="{{ url_for('login_using_discourse') }}"
|
href="{{ url_for('login_using_discourse') }}"
|
||||||
>
|
>
|
||||||
Login using YunoHost's forum
|
Login using YunoHost's forum
|
||||||
|
@ -63,7 +95,7 @@
|
||||||
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 hidden text-left text-xs sm:block">
|
<p class="ms-2 hidden text-left text-xs sm:inline-block">
|
||||||
<strong class="block font-medium">{{ user['username'] }}</strong>
|
<strong class="block font-medium">{{ user['username'] }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<i class="fa fa-caret-down fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-caret-down fa-fw" aria-hidden="true"></i>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
|
||||||
class="inline-block rounded-md border text-blue-600 border-blue-500 px-4 pt-3 text-sm font-medium hover:bg-blue-500 hover:text-white"
|
class="btn btn-primary-outline"
|
||||||
href="{{ url_for('add_to_wishlist') }}"
|
href="{{ url_for('add_to_wishlist') }}"
|
||||||
>
|
>
|
||||||
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
|
||||||
|
@ -83,12 +83,11 @@
|
||||||
<td class="text-center max-w-[5em]">
|
<td class="text-center max-w-[5em]">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="inline-block group rounded-md border text-violet-600 border-violet-500 px-2 py-2 text-xs font-medium 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"
|
||||||
>
|
>
|
||||||
123 <i class="fa fa-star-o inline-block group-hover:hidden" aria-hidden="true"></i>
|
123 <i class="fa fa-star-o inline-block group-hover:hidden" aria-hidden="true"></i>
|
||||||
<i class="fa fa-star hidden group-hover:inline-block" aria-hidden="true"></i>
|
<i class="fa fa-star hidden group-hover:inline-block" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mx-auto block rounded-md border text-white bg-blue-500 px-4 mt-5 py-2 font-medium {% if user %}hover:bg-blue-700{% endif %}"
|
class="block mx-auto btn btn-primary mt-5 {% if user %}hover:bg-blue-700{% endif %}"
|
||||||
{% if not user %}disabled{% endif %}
|
{% if not user %}disabled{% endif %}
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
|
|
Loading…
Add table
Reference in a new issue