Improve thing addition and add-ons management

Shortcut to install new bindings when adding a thing
Inbox listens to SSE and refreshes automatically
Refresh constantly the discovery results when scanning
Fix search in manual thing type selection
Improve look & feel of add-on screens and sheet

Signed-off-by: Yannick Schaus <github@schaus.net>
pull/188/head
Yannick Schaus 2020-01-29 18:35:46 +01:00 committed by Kai Kreuzer
parent 2a03c40f9e
commit 0ab0a0b691
7 changed files with 160 additions and 118 deletions

View File

@ -1,36 +1,14 @@
<template>
<f7-sheet class="demo-sheet-swipe-to-step" :opened="opened" @sheet:closed="$emit('closed')" swipe-to-close swipe-to-step backdrop
style="height:auto; --f7-sheet-bg-color: #fff;">
<f7-sheet ref="sheet" class="demo-sheet-swipe-to-step" @sheet:closed="$emit('closed')" swipe-to-close swipe-to-step backdrop>
<div class="sheet-modal-swipe-step">
<div class="swipe-handler"></div>
<div class="swipe-handler" @click="toggleSwipeStep"></div>
<f7-block-title><strong><big>{{addon.label}}</big></strong></f7-block-title>
<f7-block>
<f7-row>
<f7-col class="col-100 tablet-50">
<f7-button
outline
color="blue"
:href="addon.link"
external
target="_blank"
>Documentation</f7-button>
</f7-col>
<f7-col class="col-100 tablet-50">
<f7-button
outline
fill
color="blue"
v-if="state === 'UNINSTALLED'"
@click="install()"
>Install</f7-button>
<f7-button
outline
fill
color="red"
v-if="state === 'INSTALLED'"
@click="uninstall()"
>Uninstall</f7-button>
<f7-col class="col-100 margin-top padding-horizontal">
<f7-button large fill color="blue" v-if="state === 'UNINSTALLED'" @click="install()" >Install</f7-button>
<f7-button large fill color="red" v-if="state !== 'UNINSTALLED'" @click="uninstall()" >Uninstall</f7-button>
</f7-col>
</f7-row>
</f7-block>
@ -40,27 +18,30 @@
</div>
<f7-page-content>
<f7-block>
<p>
<strong>Version: {{addon.version}}</strong>
</p>
<f7-list>
<f7-list-item title="Version" :after="addon.version"></f7-list-item>
<f7-list-button v-if="addon.link" color="blue" external target="_blank" :href="addon.link" title="Documentation"></f7-list-button>
</f7-list>
</f7-block>
<f7-block v-if="bindingInfo.description">
<p>
<strong>Author: {{bindingInfo.author}}</strong>
</p>
<div v-html="bindingInfo.description"/>
<p>
<em>Author: {{bindingInfo.author}}</em>
</p>
</f7-block>
<f7-block v-if="!bindingInfo.description">
<!-- <f7-block v-else>
<p>No description available.</p>
</f7-block>
</f7-block> -->
</f7-page-content>
</f7-sheet>
</template>
<style lang="stylus">
.demo-sheet-swipe-to-step
height auto
.demo-sheet-swipe-to-close,
.demo-sheet-swipe-to-step {
--f7-sheet-bg-color: #fff;
--f7-sheet-border-color: transparent;
border-radius: 15px 15px 0 0;
overflow: hidden;
@ -71,7 +52,6 @@
left: 0;
width: 100%;
top: 0;
background: #fff;
cursor: pointer;
z-index: 10;
}
@ -90,15 +70,15 @@
@media (min-width: 1024px)
.demo-sheet-swipe-to-close, .demo-sheet-swipe-to-step
margin-left 150px
margin-right 150px
width: calc(100% - 300px)
margin-left 15%
margin-right 15%
width calc(100% - 30%)
@media (min-width: 1280px)
.demo-sheet-swipe-to-close, .demo-sheet-swipe-to-step
margin-left 250px
margin-right 250px
width: calc(100% - 500px)
margin-left 30%
margin-right 30%
width: calc(100% - 60%)
</style>
@ -112,23 +92,38 @@ export default {
}
},
watch: {
addonId () {
if (!this.addonId) {
this.addon = {}
this.bindingInfo = {}
return
}
this.$oh.api.get('/rest/extensions/' + this.addonId).then(data => {
this.addon = data
if (this.addon.type === 'binding' && this.addon.installed) {
this.$oh.api.get('/rest/bindings').then(data2 => {
this.bindingInfo = data2.find(b => b.id === this.addonId.replace('binding-', '')) || {}
// TODO: binding configuration stuff
})
opened (state) {
let self = this
if (state) {
if (!this.addonId) {
this.addon = {}
this.bindingInfo = {}
return
}
})
self.$f7.preloader.show()
this.$oh.api.get('/rest/extensions/' + this.addonId).then(data => {
this.addon = data
if (this.addon.type === 'binding' && this.addon.installed) {
this.$oh.api.get('/rest/bindings').then(data2 => {
this.bindingInfo = data2.find(b => b.id === this.addonId.replace('binding-', '')) || {}
self.$f7.preloader.hide()
setTimeout(() => {
self.$refs.sheet.f7Sheet.setSwipeStep()
self.$refs.sheet.f7Sheet.open()
})
// TODO: binding configuration stuff
})
} else {
self.$f7.preloader.hide()
self.$refs.sheet.f7Sheet.setSwipeStep()
self.$refs.sheet.f7Sheet.open()
}
})
} else {
self.$refs.sheet.f7Sheet.close()
}
}
},
computed: {
@ -139,6 +134,10 @@ export default {
}
},
methods: {
toggleSwipeStep () {
const self = this
self.$f7.sheet.stepToggle('.demo-sheet-swipe-to-step')
},
install () {
this.$oh.api.post('/rest/extensions/' + this.addonId + '/install', {}, 'text').then((data) => {
this.$emit('install', this.addon)

View File

@ -1,6 +1,6 @@
<template>
<f7-page @page:afterin="onPageAfterIn" @page:beforeout="stopEventSource">
<f7-navbar :title="'Add ' + addonType + ' add-on'" back-link="Back">
<f7-navbar :title="'Add ' + addonType + ' add-ons'" back-link="Back">
<f7-subnavbar :inner="false">
<f7-searchbar search-container=".search-list" search-in=".item-title" remove-diacritics :disable-button="!$theme.aurora"></f7-searchbar>
</f7-subnavbar>
@ -10,10 +10,21 @@
</f7-list>
<f7-block class="block-narrow">
<f7-col>
<f7-block-title
v-if="addons.length"
>{{addons.length}} Addon{{addons.length > 1 ? 's' : ''}} available</f7-block-title>
<f7-list media-list class="search-list searchbar-found">
<f7-block-title v-if="!ready">Loading...</f7-block-title>
<f7-block-title v-else>{{addons.length}} add-on{{addons.length > 1 ? 's' : ''}} available</f7-block-title>
<f7-list media-list v-if="!ready">
<f7-list-item
v-for="n in 10"
:key="n"
:class="`skeleton-text skeleton-effect-blink`"
title="Label of the binding"
header="BindingID"
footer="Binding version"
media-item
>
</f7-list-item>
</f7-list>
<f7-list v-else media-list class="search-list searchbar-found">
<f7-list-item
media-item
v-for="addon in addons"
@ -25,16 +36,10 @@
:after="(currentlyInstalling.indexOf(addon.id) >= 0) ? 'Installing...' : ''"
:title="addon.label"
>
<!-- <f7-icon slot="media" icon="demo-list-icon"></f7-icon> -->
</f7-list-item>
</f7-list>
</f7-col>
</f7-block>
<!-- <addon-details-popup
:addon-id="currentAddonId"
:opened="addonPopupOpened"
@closed="addonPopupOpened = false"
/> -->
<addon-details-sheet
:addon-id="currentAddonId"
:opened="addonPopupOpened"
@ -45,12 +50,10 @@
</template>
<script>
// import AddonDetailsPopup from './addon-details-popup.vue'
import AddonDetailsSheet from './addon-details-sheet.vue'
export default {
components: {
// AddonDetailsPopup,
AddonDetailsSheet
},
props: ['addonType'],
@ -58,6 +61,7 @@ export default {
return {
addons: [],
currentAddonId: null,
ready: false,
addonPopupOpened: false,
currentlyInstalling: []
}
@ -74,6 +78,7 @@ export default {
load () {
this.$oh.api.get('/rest/extensions').then(data => {
this.addons = data.filter(addon => !addon.installed && addon.type === this.addonType)
this.ready = true
this.startEventSource()
}).catch((err) => {
// sometimes we get 502 errors ('Jersey is not ready yet!'), keep trying
@ -95,6 +100,15 @@ export default {
this.stopEventSource()
this.load()
break
case 'failed':
this.$f7.toast.create({
text: `Installation of addon ${topicParts[2]} failed`,
closeButton: true,
destroyOnClose: true
}).open()
this.stopEventSource()
this.load()
break
}
}, () => {
// in case of error, maybe the SSE connection was closed by the add-ons change itself - try reloading to refresh

View File

@ -5,10 +5,23 @@
<f7-link href="add">Add</f7-link>
</f7-nav-right>-->
</f7-navbar>
<f7-block form v-if="addons.length" class="service-config block-narrow">
<f7-block form class="service-config block-narrow">
<f7-col>
<f7-block-title>{{addons.length}} Add-on{{addons.length > 1 ? 's' : ''}} installed</f7-block-title>
<f7-list media-list>
<f7-block-title v-if="!ready">Loading...</f7-block-title>
<f7-block-title v-else-if="addons.length">{{addons.length}} add-on{{addons.length > 1 ? 's' : ''}} installed</f7-block-title>
<f7-list media-list v-if="!ready">
<f7-list-item
v-for="n in 10"
:key="n"
:class="`skeleton-text skeleton-effect-blink`"
title="Label of the binding"
header="BindingID"
footer="Binding version"
media-item
>
</f7-list-item>
</f7-list>
<f7-list v-else>
<f7-list-item
media-item
link="#"
@ -30,7 +43,7 @@
</f7-list>
</f7-col>
</f7-block>
<f7-block form v-if="!addons.length" class="service-config block-narrow">
<f7-block form v-if="ready && !addons.length" class="service-config block-narrow">
<f7-col>
<f7-block strong>
<p>No add-ons of type {{addonType}} installed yet. Click the + button to add one!</p>
@ -67,6 +80,7 @@ export default {
data () {
return {
addons: [],
ready: false,
currentAddonId: null,
addonPopupOpened: false,
currentlyUninstalling: []
@ -84,6 +98,7 @@ export default {
load () {
this.$oh.api.get('/rest/extensions').then(data => {
this.addons = data.filter(addon => addon.installed && addon.type === this.addonType)
this.ready = true
this.startEventSource()
}).catch((err) => {
// sometimes we get 502 errors ('Jersey is not ready yet!'), keep trying
@ -105,6 +120,15 @@ export default {
this.stopEventSource()
this.load()
break
case 'failed':
this.$f7.toast.create({
text: `Installation of addon ${topicParts[2]} failed`,
closeButton: true,
destroyOnClose: true
}).open()
this.stopEventSource()
this.load()
break
}
}, () => {
// in case of error, maybe the SSE connection was closed by the add-ons change itself - try reloading to refresh

View File

@ -1,5 +1,5 @@
<template>
<f7-page @page:afterin="onPageAfterIn">
<f7-page @page:afterin="onPageAfterIn" @page:afterout="stopEventSource">
<f7-navbar title="Inbox" back-link="Settings" back-link-url="/settings/" back-link-force>
<f7-nav-right>
<f7-link icon-md="material:done_all" @click="toggleCheck()"
@ -42,7 +42,7 @@
<f7-block class="block-narrow">
<f7-col>
<f7-block-title><span v-if="ready">{{inbox.length}} entries</span>
<f7-block-title><span v-if="ready">{{inboxCount}} entries</span>
<div style="text-align:right; color:var(--f7-block-text-color); font-weight: normal" class="float-right">
<label @click="toggleIgnored" style="cursor:pointer">Show ignored</label> <f7-checkbox :checked="showIgnored" @change="toggleIgnored"></f7-checkbox>
</div>
@ -130,6 +130,10 @@ export default {
}
},
computed: {
inboxCount () {
if (!this.inbox) return 0
return (this.showIgnored) ? this.inbox.length : this.inbox.filter((e) => e.flag !== 'IGNORED').length
},
indexedInbox () {
const filteredInbox = (this.showIgnored) ? this.inbox : this.inbox.filter((e) => e.flag !== 'IGNORED')
if (this.groupBy === 'alphabetical') {
@ -168,6 +172,18 @@ export default {
},
onPageAfterIn () {
this.load()
this.startEventSource()
},
startEventSource () {
this.eventSource = this.$oh.sse.connect('/rest/events?topics=smarthome/inbox/*', null, (event) => {
console.log(event)
// const topicParts = event.topic.split('/')
this.load()
})
},
stopEventSource () {
this.$oh.sse.close(this.eventSource)
this.eventSource = null
},
openEntryActions (e, entry) {
if (this.showCheckboxes) {

View File

@ -49,13 +49,16 @@
</f7-list>
</f7-col>
</f7-block>
<f7-block v-if="ready && !bindings.length" class="block-narrow">
<f7-col>
<f7-col v-if="ready && !bindings.length">
<f7-block strong>
<p>No bindings available.</p>
</f7-block>
</f7-col>
<f7-col>
<f7-list>
<f7-list-button color="blue" title="Install New Bindings" href="/settings/addons/binding/add" />
</f7-list>
</f7-col>
</f7-block>
</f7-page>
</template>

View File

@ -13,13 +13,6 @@
></f7-searchbar>
</f7-subnavbar>
</f7-navbar>
<!-- <f7-list-index
ref="listIndex"
list-el=".thing-type-list"
:scroll-list="true"
:label="true"
></f7-list-index> -->
<f7-block class="block-narrow">
<f7-col>
<div v-if="discoverySupported" class="display-flex justify-content-center">
@ -29,18 +22,7 @@
</div>
<p class="margin-left margin-right" style="height: 30px" id="scan-progress"></p>
<f7-block-title v-if="discoverySupported && scanResults.length">Discovered Things</f7-block-title>
<!-- <f7-list class="col thing-type-list" v-if="scanning">
<f7-list-item title="Scanning for things...">
<f7-preloader slot="media" :size="42"></f7-preloader>
</f7-list-item>
</f7-list> -->
<!-- <f7-list class="col thing-type-list" v-if="ready && discoverySupported && !scanning && !scanResults.length">
<f7-list-item
title="No discovery results.">
<f7-button slot="after" @click="scan()">Retry</f7-button>
</f7-list-item>
</f7-list> -->
<f7-list class="col thing-type-list" v-if="scanResults.length">
<f7-list class="col" v-if="scanResults.length">
<f7-list-item v-for="entry in scanResults"
:key="entry.thingUID"
:link="true"
@ -54,7 +36,7 @@
</f7-list>
<f7-block-title>Add Manually</f7-block-title>
<f7-list>
<f7-list class="thing-type-list">
<ul v-if="!ready">
<f7-list-item
v-for="n in 10"
@ -67,21 +49,23 @@
>
</f7-list-item>
</ul>
<f7-list-item v-else v-for="thingType in thingTypes"
:key="thingType.UID"
:link="thingType.UID"
:title="thingType.label"
:footer="thingType.description"
:header="thingType.UID"
:badge="thingType.bridge ? 'Bridge' : ''" badge-color="blue"
media-item
>
</f7-list-item>
<ul v-else>
<f7-list-item v-for="thingType in thingTypes"
:key="thingType.UID"
:link="thingType.UID"
:title="thingType.label"
:footer="thingType.description"
:header="thingType.UID"
:badge="thingType.bridge ? 'Bridge' : ''" badge-color="blue"
media-item
>
</f7-list-item>
</ul>
</f7-list>
</f7-col>
</f7-block>
<f7-block v-if="!loading && !thingTypes.length" class="block-narrow">
<f7-block v-if="!loading && ready && !thingTypes.length" class="block-narrow">
<f7-col>
<f7-block strong>
<p>No thing types can be added with this binding.</p>
@ -128,8 +112,9 @@ export default {
return a.label.localeCompare(b.label)
})
this.loading = false
this.ready = true
this.initSearchbar = true
this.ready = true
this.loadInbox()
this.$oh.api.get('/rest/discovery').then((data) => {
if (data.indexOf(this.bindingId) >= 0) {
this.discoverySupported = true
@ -142,13 +127,13 @@ export default {
this.scanning = true
this.$oh.api.postPlain('/rest/discovery/bindings/' + this.bindingId + '/scan', null, 'text/plain', 'text/plain').then((data) => {
try {
this.loadInbox()
this.scanTimeout = parseInt(data)
this.scanProgress = 0
let progressBarEl = this.$f7.progressbar.show('#scan-progress', 0, 'blue')
this.intervalId = setInterval(() => {
this.scanProgress += 1
this.$f7.progressbar.set(progressBarEl, this.scanProgress * 100 / this.scanTimeout)
this.loadInbox()
}, 1000)
setTimeout(() => {
this.scanning = false

View File

@ -244,10 +244,11 @@ export default {
this.$f7.dialog.alert('Please configure the channel to link')
return
}
if (!this.itemTypeCompatible()) {
this.$f7.dialog.alert('The channel and item type are not compatible')
return
}
// temporarily disabled
// if (!this.itemTypeCompatible()) {
// this.$f7.dialog.alert('The channel and item type are not compatible')
// return
// }
if (this.createItem) {
this.$oh.api.put('/rest/items/' + this.newItem.name, this.newItem).then((data) => {