Hass.io improve store (#875)

* Hass.io improve store

* Improve code
This commit is contained in:
c727 2018-02-09 22:48:58 +01:00 committed by GitHub
parent 6ce72444ae
commit ac628787ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 200 additions and 436 deletions

View File

@ -1,45 +1,46 @@
<link rel="import" href="../../bower_components/polymer/polymer-element.html"> <link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html"> <link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/paper-item/paper-item.html">
<link rel="import" href="../../bower_components/paper-item/paper-item-body.html">
<link rel='import' href='../../src/util/hass-mixins.html'> <link rel='import' href='../../src/util/hass-mixins.html'>
<link rel='import' href='../../src/components/hassio-card-content.html'>
<link rel='import' href='../../src/resources/hassio-style.html'>
<dom-module id="hassio-addon-repository"> <dom-module id="hassio-addon-repository">
<template> <template>
<style include="iron-flex ha-style"> <style include="iron-flex ha-style hassio-style">
:host,
paper-card { paper-card {
display: block;
}
paper-item {
cursor: pointer; cursor: pointer;
} }
a.repo {
display: block;
color: var(--primary-text-color);
}
</style> </style>
<paper-card heading='[[repo.name]]'> <template is='dom-if' if='[[addons.length]]'>
<template is='dom-if' if='[[computeShowIntro(repo)]]'> <div class='card-group'>
<div class='title'>
[[repo.name]]
<div class='description'>
Maintained by [[repo.maintainer]]
<a class='repo' href='[[repo.url]]' target='_blank'>[[repo.url]]</a>
</div>
</div>
<template is='dom-repeat' items='[[addons]]' as='addon' sort='sortAddons'>
<paper-card on-click='addonTapped'>
<div class='card-content'> <div class='card-content'>
<template is='dom-if' if='[[repo.maintainer]]'> <hassio-card-content
Maintained by [[repo.maintainer]]. title='[[addon.name]]'
</template> description='[[addon.description]]'
<template is='dom-if' if='[[repo.url]]'> icon='[[computeIcon(addon)]]'
&nbsp; icon-title='[[computeIconTitle(addon)]]'
<a href='[[repo.url]]' target='_blank'>Visit repository website</a>. icon-class='[[computeIconClass(addon)]]'
></hassio-card-content>
</div>
</paper-card>
</template> </template>
</div> </div>
</template> </template>
<template is='dom-repeat' items='[[addons]]' as='addon'>
<paper-item>
<paper-item-body two-line on-tap='addonTapped'>
<div>[[addon.name]]</div>
<div secondary>[[addon.description]]</div>
</paper-item-body>
[[computeInstallStatus(addon)]]
</paper-item>
</template>
</paper-card>
</template> </template>
</dom-module> </dom-module>
@ -49,31 +50,31 @@ class HassioAddonRepository extends window.hassMixins.EventsMixin(Polymer.Elemen
static get properties() { static get properties() {
return { return {
repo: { repo: Object,
type: Object, addons: Array,
},
addons: {
type: Array,
},
}; };
} }
computeShowIntro(repo) { sortAddons(a, b) {
return repo.url || repo.maintainer; return a.name < b.name ? -1 : 1;
} }
computeInstallStatus(addon) { computeIcon(addon) {
if (!addon.installed) { return addon.installed && addon.installed !== addon.version ? 'mdi:arrow-up-bold-circle' : 'mdi:puzzle';
return 'Not installed';
} else if (addon.installed !== addon.version) {
return addon.installed + ' (update available)';
} }
return addon.installed;
computeIconTitle(addon) {
if (addon.installed) return addon.installed !== addon.version ? 'New version available' : 'Add-on is installed';
return 'Add-on is not installed';
}
computeIconClass(addon) {
if (addon.installed) return addon.installed !== addon.version ? 'update' : 'installed';
return '';
} }
addonTapped(ev) { addonTapped(ev) {
history.pushState(null, null, '/hassio/store/' + this.addons[ev.model.index].slug); history.pushState(null, null, '/hassio/addon/' + ev.model.addon.slug);
this.fire('location-changed'); this.fire('location-changed');
} }
} }

View File

@ -1,123 +0,0 @@
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel='import' href='../../src/util/hass-mixins.html'>
<link rel="import" href="./hassio-repositories-editor.html">
<link rel="import" href="./hassio-addon-repository.html">
<dom-module id="hassio-addon-store-overview">
<template>
<style include="iron-flex ha-style">
paper-card {
display: block;
}
.content {
padding: 24px 0 32px;
max-width: 600px;
margin: 0 auto;
}
hassio-addon-repository {
margin-top: 24px;
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<paper-icon-button
icon='mdi:arrow-left'
on-tap='backTapped'
></paper-icon-button>
<div main-title>Hass.io Add-Ons</div>
<paper-icon-button
icon="mdi:refresh"
on-tap="refreshTapped"
></paper-icon-button>
</app-toolbar>
</app-header>
<div class='content'>
<hassio-repositories-editor
hass='[[hass]]'
repos='[[repos]]'
></hassio-repositories-editor>
<template is='dom-repeat' items='[[repos]]' as='repo'>
<hassio-addon-repository
repo='[[repo]]'
addons='[[computeAddOns(repo.slug)]]'
selected-addon='{{selectedAddon}}'
></hassio-addon-repository>
</template>
</div>
</app-header-layout>
</template>
</dom-module>
<script>
class HassioAddonStoreOverview extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'hassio-addon-store-overview'; }
static get properties() {
return {
hass: {
type: Object,
},
selectedAddon: {
type: String,
value: null,
notify: true,
},
addons: {
type: Array,
value: [],
},
repos: {
type: Array,
},
supervisorInfo: {
type: Object,
}
};
}
computeAddOns(repo) {
return this.addons.filter(function (addon) {
return addon.repository === repo;
}).sort(function (addonA, addonB) {
if (addonA.name < addonB.name) {
return -1;
}
if (addonA.name > addonB.name) {
return 1;
}
return 0;
}).sort(function (addonA, addonB) {
if (addonA.installed && !addonB.installed) {
return -1;
}
if (!addonA.installed && addonB.installed) {
return 1;
}
return 0;
});
}
refreshTapped() {
this.fire('hassio-store-refresh');
}
backTapped() {
history.back();
}
}
customElements.define(HassioAddonStoreOverview.is, HassioAddonStoreOverview);
</script>

View File

@ -1,145 +0,0 @@
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/app-layout/app-header-layout/app-header-layout.html">
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel='import' href='../../src/util/hass-mixins.html'>
<link rel="import" href="../../src/components/buttons/ha-call-api-button.html">
<dom-module id="hassio-addon-store-view">
<template>
<style include="iron-flex ha-style">
paper-card {
display: block;
margin-bottom: 32px;
}
.content {
padding: 24px 0 32px;
max-width: 600px;
margin: 0 auto;
}
.addon-header {
@apply --paper-font-headline;
}
.addon-light {
color: var(--secondary-text-color);
}
.addon-version {
float: right;
font-size: 15px;
vertical-align: middle;
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<paper-icon-button
icon='mdi:arrow-left'
on-tap='backTapped'
></paper-icon-button>
<div main-title>[[addon.name]]</div>
</app-toolbar>
</app-header>
<div class='content'>
<paper-card>
<div class="card-content">
<div class="addon-header">[[addon.name]]
<div class="addon-version addon-light">
v[[addon.version]]
</div>
</div>
<p>[[addon.description]]</p>
<p class='addon-light'>
Made available via repository [[addon.repository]].
</p>
<template is='dom-if' if='[[addon.url]]'>
<p><a target='_blank' href='[[addon.url]]'>Visit website</a></p>
</template>
<template is='dom-if' if='[[addon.build]]'>
<p class='addon-light'>
This add-on will built locally on the device.
</p>
</template>
</div>
<div class="card-actions">
<template is='dom-if' if='[[addon.installed]]'>
<paper-button on-tap='openAddon'>Open</paper-button>
</template>
<template is='dom-if' if='[[!addon.installed]]'>
<ha-call-api-button
hass='[[hass]]'
path="[[pathInstall(addon)]]"
>Install</ha-call-api-button>
</template>
</div>
</paper-card>
<paper-card>
<div class='card-content'>
<div class='addon-header'>Repository: [[repo.name]]</div>
<p>Maintainer: [[repo.maintainer]]</p>
<p><a target='_blank' href='[[repo.url]]'>Visit website</a></p>
</div>
</paper-card>
</div>
</app-header-layout>
</template>
</dom-module>
<script>
class HassioAddonStoreView extends window.hassMixins.EventsMixin(Polymer.Element) {
static get is() { return 'hassio-addon-store-view'; }
static get properties() {
return {
hass: {
type: Object,
},
selectedAddon: {
type: String,
value: null,
notify: true,
},
addon: {
type: Object,
},
repo: {
type: Object,
},
};
}
ready() {
super.ready();
this.addEventListener('hass-api-called', ev => this.apiCalled(ev));
}
pathInstall(addon) {
return addon && 'hassio/addons/' + addon.slug + '/install';
}
apiCalled(ev) {
if (ev.detail.success) {
this.openAddon();
}
}
openAddon() {
history.pushState(null, null, '/hassio/addon/' + this.addon.slug);
this.fire('location-changed');
}
backTapped() {
history.back();
}
}
customElements.define(HassioAddonStoreView.is, HassioAddonStoreView);
</script>

View File

@ -3,38 +3,55 @@
<link rel="import" href="../../bower_components/app-layout/app-header/app-header.html"> <link rel="import" href="../../bower_components/app-layout/app-header/app-header.html">
<link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html"> <link rel="import" href="../../bower_components/app-layout/app-toolbar/app-toolbar.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html"> <link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../../bower_components/app-route/app-route.html">
<link rel="import" href="../../src/components/ha-menu-button.html"> <link rel='import' href='../../src/util/hass-mixins.html'>
<link rel="import" href="./hassio-repositories-editor.html"> <link rel="import" href="./hassio-repositories-editor.html">
<link rel="import" href="./hassio-addon-repository.html"> <link rel="import" href="./hassio-addon-repository.html">
<link rel="import" href="./hassio-addon-store-view.html">
<link rel="import" href="./hassio-addon-store-overview.html">
<dom-module id="hassio-addon-store"> <dom-module id="hassio-addon-store">
<template> <template>
<app-route <style include="iron-flex ha-style">
route='[[route]]' paper-card {
pattern='/:addon' display: block;
data="{{_routeData}}" }
active="{{_routeMatches}}" .content {
></app-route> padding: 24px 0 32px;
<template is='dom-if' if='[[_routeMatches]]' restamp> max-width: 600px;
<hassio-addon-store-view margin: 0 auto;
hass='[[hass]]' }
addon='[[selectedAddonObject]]'
repo='[[computeActiveRepo(repos, selectedAddonObject)]]'
></hassio-addon-store-view>
</template>
<template is='dom-if' if='[[!_routeMatches]]'> hassio-addon-repository {
<hassio-addon-store-overview margin-top: 24px;
}
</style>
<app-header-layout has-scrolling-region>
<app-header slot="header" fixed>
<app-toolbar>
<paper-icon-button
icon='mdi:arrow-left'
on-tap='backTapped'
></paper-icon-button>
<div main-title>Add-on store</div>
<paper-icon-button
icon="mdi:refresh"
on-tap="refreshTapped"
></paper-icon-button>
</app-toolbar>
</app-header>
<hassio-repositories-editor
hass='[[hass]]' hass='[[hass]]'
addons='[[addons]]'
repos='[[repos]]' repos='[[repos]]'
></hassio-addon-store-overview> ></hassio-repositories-editor>
<template is='dom-repeat' items='[[repos]]' as='repo' sort='sortRepos'>
<hassio-addon-repository
repo='[[repo]]'
addons='[[computeAddons(repo.slug)]]'
></hassio-addon-repository>
</template> </template>
</app-header-layout>
</template> </template>
</dom-module> </dom-module>
@ -44,37 +61,13 @@ class HassioAddonStore extends Polymer.Element {
static get properties() { static get properties() {
return { return {
hass: { hass: Object,
type: Object, addons: Array,
}, repos: Array,
visible: { visible: {
type: Boolean, type: Boolean,
observer: '_visibleChanged', observer: 'visibleChanged',
}, },
route: Object,
_routeData: Object,
_routeMatches: Boolean,
selectedAddonObject: {
type: Object,
computed: 'computeActiveAddon(addons, _routeData.addon)',
},
addons: {
type: Array,
value: [],
},
repos: {
type: Array,
value: []
},
supervisorInfo: {
type: Object,
}
}; };
} }
@ -86,52 +79,60 @@ class HassioAddonStore extends Polymer.Element {
apiCalled(ev) { apiCalled(ev) {
if (ev.detail.success) { if (ev.detail.success) {
this._loadData(); this.loadData();
} }
} }
_visibleChanged(visible) { sortRepos(a, b) {
if (a.slug === 'local') {
return -1;
} else if (b.slug === 'local') {
return 1;
} else if (a.slug === 'core') {
return -1;
} else if (b.slug === 'core') {
return 1;
}
return a.name < b.name ? -1 : 1;
}
computeAddons(repo) {
return this.addons.filter(function (addon) {
return addon.repository === repo;
});
}
visibleChanged(visible) {
if (visible) { if (visible) {
this._loadData(); this.loadData();
} }
} }
_loadData() { loadData() {
this.hass.callApi('get', 'hassio/addons') this.hass.callApi('get', 'hassio/addons')
.then(function (info) { .then((info) => {
this.addons = info.data.addons; this.addons = info.data.addons;
this.repos = info.data.repositories; this.repos = info.data.repositories;
}.bind(this), function () { }, () => {
this.addons = []; this.addons = [];
this.repos = []; this.repos = [];
}.bind(this)); });
} }
computeActiveAddon(addons, selectedAddon) {
for (var i = 0; i < addons.length; i++) {
if (addons[i].slug === selectedAddon) {
return addons[i];
}
}
return null;
}
computeActiveRepo(repos, selectedAddonObject) {
if (!selectedAddonObject) return null;
for (var i = 0; i < repos.length; i++) {
if (repos[i].slug === selectedAddonObject.repository) {
return repos[i];
}
}
return null;
}
refreshData() { refreshData() {
this.hass.callApi('post', 'hassio/addons/reload') this.hass.callApi('post', 'hassio/addons/reload')
.then(function () { .then(() => {
this._loadData(); this.loadData();
}.bind(this)); });
}
refreshTapped() {
this.fire('hassio-store-refresh');
}
backTapped() {
history.back();
} }
} }

View File

@ -1,36 +1,70 @@
<link rel="import" href="../../bower_components/polymer/polymer-element.html"> <link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/paper-card/paper-card.html"> <link rel="import" href="../../bower_components/paper-card/paper-card.html">
<link rel="import" href="../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html"> <link rel='import' href='../../bower_components/paper-input/paper-input.html'>
<link rel='import' href='../../bower_components/iron-icon/iron-icon.html'>
<link rel="import" href="../../src/components/buttons/ha-call-api-button.html"> <link rel="import" href="../../src/components/buttons/ha-call-api-button.html">
<link rel='import' href='../../src/components/hassio-card-content.html'>
<link rel='import' href='../../src/resources/hassio-style.html'>
<dom-module id="hassio-repositories-editor"> <dom-module id="hassio-repositories-editor">
<template> <template>
<style include="ha-style"> <style include="ha-style hassio-style">
:host { .add {
display: block; padding: 12px 16px;
} }
paper-card { iron-icon {
display: block; color: var(--secondary-text-color);
margin-right: 16px;
display: inline-block;
} }
iron-autogrow-textarea { paper-input {
width: 100%; width: calc(100% - 49px);
display: inline-block;
} }
</style> </style>
<paper-card heading='Add-On Repositories'> <div class='card-group'>
<div class="card-content"> <div class='title'>
<p>Configure which add-on repositories to fetch data from. One repository per line.</p> Repositories
<iron-autogrow-textarea value="{{options}}"></iron-autogrow-textarea> <div class='description'>
Configure which add-on repositories to fetch data from:
</div> </div>
<div class="card-actions"> </div>
<template id='list' is='dom-repeat' items='[[repoList]]' as='repo' sort='sortRepos'>
<paper-card>
<div class='card-content'>
<hassio-card-content
title='[[repo.name]]'
description='[[repo.url]]'
icon='mdi:github-circle'
></hassio-card-content>
</div>
<div class='card-actions'>
<ha-call-api-button <ha-call-api-button
hass='[[hass]]' hass='[[hass]]'
data='[[computeOptionsData(options)]]' path='hassio/supervisor/options'
path="hassio/supervisor/options" data='[[computeRemoveRepoData(repoList, repo.url)]]'
>Save</ha-call-api-button> class='warning'
>Remove</ha-call-api-button>
</div> </div>
</paper-card> </paper-card>
</template> </template>
<paper-card>
<div class='card-content add'>
<iron-icon icon='mdi:github-circle'></iron-icon>
<paper-input label='Add new repository by URL' value='{{repoUrl}}'></paper-input>
</div>
<div class='card-actions'>
<ha-call-api-button
hass='[[hass]]'
path='hassio/supervisor/options'
data='[[computeAddRepoData(repoList, repoUrl)]]'
>Add</ha-call-api-button>
</div>
</paper-card>
</div>
</template>
</dom-module> </dom-module>
<script> <script>
@ -39,36 +73,34 @@ class HassioRepositoriesEditor extends Polymer.Element {
static get properties() { static get properties() {
return { return {
hass: { hass: Object,
type: Object,
},
options: {
type: String,
value: '',
},
repos: { repos: {
type: Array, type: Array,
observer: 'reposChanged', observer: 'reposChanged',
}, },
repoList: Array,
repoUrl: String,
}; };
} }
reposChanged(repos) { reposChanged(repos) {
this.options = repos this.repoList = repos.filter(repo => repo.slug !== 'core' && repo.slug !== 'local');
.filter(function (el) { return el.source !== null; }) this.repoUrl = '';
.map(function (el) { return el.source; })
.join('\n');
} }
computeOptionsData(options) { sortRepos(a, b) {
var parsed = options.trim(); return a.name < b.name ? -1 : 1;
parsed = parsed ? }
parsed.split('\n').map(function (ent) { return ent.trim(); }) : [];
return { computeRemoveRepoData(repoList, url) {
addons_repositories: parsed, const list = repoList.filter(repo => repo.url !== url).map(repo => repo.url);
}; return { addons_repositories: list };
}
computeAddRepoData(repoList, url) {
const list = repoList.map(repo => repo.url);
list.push(url);
return { addons_repositories: list };
} }
} }

View File

@ -58,9 +58,7 @@
<hassio-addon-store <hassio-addon-store
page-name='store' page-name='store'
route='[[_routeTail]]'
hass='[[hass]]' hass='[[hass]]'
supervisor-info='[[supervisorInfo]]'
></hassio-addon-store> ></hassio-addon-store>
<hassio-supervisor <hassio-supervisor