Merge pull request #242 from YunoHost/high-quality-apps-and-maintained-states

High quality apps and maintained states
This commit is contained in:
Alexandre Aubin 2019-06-04 00:15:09 +02:00 committed by GitHub
commit 8e8113d743
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 41 deletions

View file

@ -588,6 +588,10 @@ input[type='radio'].nice-radio {
}
}
.label-best {
background-color: darkorchid;
}
// only one card for small screens
.app-card {
width: 100%;

View file

@ -18,7 +18,10 @@
});
function levelToColor(level) {
if (level >= 3) {
if (level >= 8) {
return 'best';
}
else if (level > 4) {
return 'success';
}
else if (level >= 1) {
@ -32,7 +35,10 @@
}
function stateToColor(state) {
if (state === "working" || state === "official") {
if (state === "high-quality") {
return 'best';
}
else if (state === "working") {
return 'success';
}
else {
@ -40,6 +46,21 @@
}
}
function maintainedStateToColor(state) {
if ( state === "request_help" ) {
return 'info';
}
else if ( state === "request_adoption" ) {
return 'warning';
}
else if ( state === "orphaned" ) {
return 'danger';
}
else {
return 'success';
}
}
function combineColors(stateColor, levelColor, installable) {
if (stateColor === "danger" || levelColor === "danger") {
return 'danger';
@ -53,36 +74,83 @@
}
}
function extractMaintainer(manifest) {
if (manifest.maintainer === undefined)
{
if ((manifest.developer !== undefined) && (manifest.developer.name !== undefined))
{
return manifest.developer.name;
}
else
{
return "?";
}
}
else if (Array.isArray(manifest.maintainer))
{
maintainers = [];
manifest.maintainer.forEach(function(maintainer) {
if (maintainer.name !== undefined)
{
maintainers.push(maintainer.name);
}
});
return maintainers.join(', ');
}
else if (manifest.maintainer.name !== undefined)
{
return manifest.maintainer.name;
}
else
{
return "?";
}
}
// List available apps
app.get('#/apps/install', function (c) {
c.api('/apps', function (data) { // http://api.yunohost.org/#!/app/app_list_get_8
c.api('/apps?raw', function (dataraw) { // http://api.yunohost.org/#!/app/app_list_get_8
var apps = []
$.each(data['apps'], function(k, v) {
if (dataraw[v['id']]['state'] === "validated")
app = dataraw[v['id']];
app.level = parseInt(app.level);
if (app.high_quality && app.level > 7)
{
dataraw[v['id']]['state'] = "official";
app.state = "high-quality";
}
if ( app.maintained === false )
{
app.maintained = "orphaned";
}
else if ( app.maintained === true )
{
app.maintained = "maintained";
}
var state = dataraw[v['id']]['state'];
var levelFormatted = parseInt(dataraw[v['id']]['level']);
var isWorking = (state === 'working' || state === 'official') && levelFormatted > 0;
// Keep only the first instance of each app and remove community not working apps
if (!v['id'].match(/__[0-9]{1,5}$/) && (dataraw[v['id']]['repository'] === 'yunohost' || state !== 'notworking')) {
dataraw[v['id']]['installable'] = (!v['installed'] || dataraw[v['id']].manifest.multi_instance)
dataraw[v['id']]['isCommunity'] = !(dataraw[v['id']]['repository'] === 'yunohost');
dataraw[v['id']]['levelFormatted'] = isNaN(levelFormatted) ? '?' : levelFormatted;
dataraw[v['id']]['levelColor'] = levelToColor(levelFormatted);
dataraw[v['id']]['stateColor'] = stateToColor(state);
dataraw[v['id']]['installColor'] = combineColors(dataraw[v['id']]['stateColor'], dataraw[v['id']]['levelColor']);
dataraw[v['id']]['displayLicense'] = (dataraw[v['id']]['manifest']['license'] !== undefined
&& dataraw[v['id']]['manifest']['license'] !== 'free');
dataraw[v['id']]['updateDate'] = dataraw[v['id']]['lastUpdate'] * 1000 || 0;
dataraw[v['id']]['isSafe'] = (dataraw[v['id']]['installColor'] !== 'danger');
dataraw[v['id']]['isWorking'] = isWorking ? "isworking" : "notFullyWorking";
app.manifest.maintainer = extractMaintainer(app.manifest);
var isWorking = (app.state === 'working' || app.state === "high-quality") && app.level > 0;
jQuery.extend(dataraw[v['id']], v);
apps.push(dataraw[v['id']]);
// Keep only the first instance of each app and remove not working apps
if (!v['id'].match(/__[0-9]{1,5}$/) && (app.state !== 'notworking')) {
app.installable = (!v.installed || app.manifest.multi_instance)
app.levelFormatted = isNaN(app.level) ? '?' : app.level;
app.levelColor = levelToColor(app.level);
app.stateColor = stateToColor(app.state);
app.maintainedColor = maintainedStateToColor(app.maintained);
app.installColor = combineColors(app.stateColor, app.levelColor);
app.updateDate = app.lastUpdate * 1000 || 0;
app.isSafe = (app.installColor !== 'danger');
app.isWorking = isWorking ? "isworking" : "notFullyWorking";
app.isHighQuality = (app.state === "high-quality") ? "isHighQuality" : "";
app.decentQuality = (app.level > 4)?"decentQuality":"badQuality";
jQuery.extend(app, v);
apps.push(app);
}
});
@ -107,8 +175,8 @@
return inputMatch && classMatch;
},
// Keep only official apps at first render
cardGrid.isotope({ filter: '.isworking' });
// Default filter is 'decent quality apps'
cardGrid.isotope({ filter: '.decentQuality' });
jQuery('.dropdownFilter').on('click', function() {
// change dropdown label
@ -472,7 +540,8 @@
app.helper('appInstallForm', function(appId, manifest, params) {
var data = {
id: appId,
manifest: manifest
manifest: manifest,
displayLicense: (manifest['license'] !== undefined && manifest['license'] !== 'free')
};
formatYunoHostStyleArguments(data.manifest.arguments.install, params);

View file

@ -29,16 +29,19 @@
"app_install_cancel": "Installation cancelled.",
"app_install_custom_no_manifest": "No manifest.json file",
"app_list": "App list",
"app_license": "License of the app",
"app_level": "App level",
"app_make_default": "Make default",
"app_no_actions": "This application doesn't have any actions",
"app_repository": "Application origin: ",
"app_state": "Application state: ",
"app_state_inprogress": "In progress",
"app_state_notworking": "Not working",
"app_state_official": "Official",
"app_state_working": "Working",
"app_state_inprogress": "in progress",
"app_state_inprogress_explanation": "This maintainer of this app declared that this application is not ready yet for production use. BE CAREFUL!",
"app_state_notworking": "not working",
"app_state_notworking_explanation": "This maintainer of this app declared it as 'not working'. IT WILL BREAK YOUR SYSTEM!",
"app_state_high-quality": "high quality",
"app_state_high-quality_explanation": "This app is well-integrated with YunoHost. It has been (and is!) peer-reviewed by the YunoHost app team. It can be expected to be safe and maintained on the long-term.",
"app_state_working": "working",
"app_state_working_explanation": "The maintainer of this app declared it as 'working'. It means that it should be functional (c.f. application level) but is not necessarily peer-reviewed, it may still contain issues or is not fully integrated with YunoHost.",
"application": "Application",
"applications": "Applications",
"archive_empty": "Empty archive",
@ -189,6 +192,7 @@
"label": "Label",
"label_for_manifestname": "Label for %s",
"level": "level",
"license": "License",
"loading": "Loading …",
"local_archives": "Local archives",
"local_ip": "Local IP",
@ -199,6 +203,8 @@
"logout": "Logout",
"mailbox_quota_description": "For example, 700M is a CD, 4700M is a DVD.",
"mailbox_quota_placeholder": "Leave empty or set to 0 to disable.",
"maintained": "maintained",
"maintained_details": "This app was maintained by its maintainer in the last few months.",
"manage_apps": "Manage apps",
"manage_domains": "Manage domains",
"manage_users": "Manage users",
@ -226,10 +232,13 @@
"no_user_to_add": "No more users to add.",
"non_compatible_api": "Non-compatible API",
"ok": "OK",
"only_official_apps": "Only official apps",
"only_highquality_apps": "Only high-quality apps",
"only_working_apps": "Only working apps",
"only_decent_quality_apps": "Only decent quality apps",
"open": "Open",
"operations": "Operations",
"orphaned": "not maintained",
"orphaned_details": "This app is not maintained anymore. It may still be working but won't receive any upgrade. Feel free to come and revive it!",
"os": "OS",
"password": "Password",
"password_confirmation": "Password confirmation",
@ -279,6 +288,10 @@
"reception": "Reception",
"refresh_app_list": "Refresh list",
"remove_access": "Remove access",
"request_adoption": "waiting adoption",
"request_adoption_details": "The current maintainer would like to stop maintaining this app. Feel free to propose yourself as the new maintainer!",
"request_help": "need help",
"request_help_details": "The current maintainer would like some help with the maintainance of this app. Feel free to come contribute on it!",
"restore": "Restore",
"run": "Run",
"running": "Running",

View file

@ -19,6 +19,10 @@
<dd>{{id}}</dd>
<dt>{{t 'description'}}</dt>
<dd>{{description}}</dd>
{{#displayLicense}}
<dt>{{t 'license'}}</dt>
<dd>{{manifest.license}}</dd>
{{/displayLicense}}
<dt>{{t 'version'}}</dt>
<dd>{{manifest.version}}</dd>
<dt>{{t 'multi_instance'}}</dt>

View file

@ -20,11 +20,12 @@
<input type="text" id="filter-app-cards" class="form-control" role="textbox" placeholder="{{t 'search_for_apps'}}" aria-describedby="basic-addon0"/>
<div class="input-group-btn">
<button type="button" role="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="app-cards-list-filter-text">{{t 'only_working_apps'}}</span> <span class="caret"></span>
<span id="app-cards-list-filter-text">{{t 'only_decent_quality_apps'}}</span> <span class="caret"></span>
</button>
<ul id="dropdownFilter" class="dropdown-menu" data-filter="isworking" role="menu">
<ul id="dropdownFilter" class="dropdown-menu" data-filter="decentQuality" role="menu">
<li role="presentation" class="button dropdownFilter" data-filter="isHighQuality"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_highquality_apps'}}</a></li>
<li role="presentation" class="button dropdownFilter" data-filter="decentQuality"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_decent_quality_apps'}}</a></li>
<li role="presentation" class="button dropdownFilter" data-filter="isworking"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_working_apps'}}</a></li>
<li role="presentation" class="button dropdownFilter" data-filter="official"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'only_official_apps'}}</a></li>
<li role="presentation" class="button dropdownFilter" data-filter="*"><a class="menu-item" role="menu-item" tabindex="-1">{{t 'all_apps'}}</a></li>
</ul>
</div>
@ -34,21 +35,19 @@
<div class="list-group grid">
{{#apps}}
<div class="app-card panel panel-default {{status}} {{state}} {{isWorking}} {{level}}-level">
<div class="app-card panel panel-default {{status}} {{state}} {{isWorking}} {{isHighQuality}} {{decentQuality}} {{level}}-level">
<div class="panel-body">
<h2 class="app-title">{{name}}</h2>
<div class="category">
{{#isCommunity}} <span class="label label-info label-as-badge app-status">{{t 'community'}}</span>{{/isCommunity}}
<span class="label label-{{stateColor}} label-as-badge app-state">{{t state}}</span>
<span class="label label-{{stateColor}} label-as-badge app-state" title="{{t (concat 'app_state_' state '_explanation') }}">{{t (concat 'app_state_' state) }}</span>
<a target="_BLANK" href="https://yunohost.org/#/packaging_apps_levels"><span class="label label-{{levelColor}} label-as-badge app-level" title="{{t 'app_level'}}">{{t 'level'}} {{levelFormatted}}</span></a>
{{#displayLicense}}<span class="label label-default app-license" title="{{t 'app_license'}}">{{license}}</span>{{/displayLicense}}
<span class="label label-{{maintainedColor}} label-as-badge maintained-status" title="{{t (concat maintained '_details') }}"> {{t maintained}}</span>
</div>
<div class="app-card-desc">{{description}}</div>
</div>
<div class="app-card-date-maintainer">
<i class="fa-refresh"></i> {{formatDate updateDate day="numeric" month="long" year="numeric"}} -
{{#maintained}}<span title="{{t 'current_maintainer_title'}}" class="maintained"></span><i class="fa-user"></i> {{manifest.maintainer.name}}</span>{{/maintained}}
{{^maintained}}<i class="fas fa-warning"></i> {{t 'unmaintained'}}{{/maintained}}
<span title="{{t 'current_maintainer_title'}}" class="maintained"></span><i class="fa-user"></i> {{manifest.maintainer}}</span>
</div>
<div class="btn-group" role="group">
<a href="{{git.url}}" target="_BLANK" type="button" role="button" class="btn btn-default col-xs-4">