Skip to content

Commit

Permalink
feat(ui): refactor add remove dialog (#475)
Browse files Browse the repository at this point in the history
* feat: Improved add remove device UX

* Lint cleanup

* More lint

* Fix timer

* use socket events

* changes suggested by @robertsLando

* Fix node added event

* lint fix

* cleanup debug

* fix: events bind/unbind, var names and better code style

* fix: clear dialog alert state on close

* feat(ui): refactor move code into dialog

* feat(ui): refactor @robertsLando requested changes

Co-authored-by: Daniel Lando <daniel.sorridi@gmail.com>
  • Loading branch information
jarrettv and robertsLando authored Feb 8, 2021
1 parent d69560f commit 0ecbea6
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 141 deletions.
118 changes: 4 additions & 114 deletions src/components/ControlPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,9 @@

<DialogAddRemove
v-model="addRemoveShowDialog"
:status="addRemoveStatus"
:alert="addRemoveAlert"
:nodeAddedOrRemoved="addRemoveNode"
@close="onAddRemoveClose"
@action="onAddRemoveAction"
@apiRequest="apiRequest"
/>

<nodes-table
Expand Down Expand Up @@ -104,66 +103,12 @@ export default {
return this.appInfo.controllerStatus
}
},
watch: {
addRemoveEndDate (newVal) {
if (this.addRemoveTimer) {
clearInterval(this.addRemoveTimer)
}
this.addRemoveTimer = setInterval(() => {
const now = new Date()
const s = Math.trunc((this.addRemoveEndDate - now) / 1000)
if (this.addRemoveStatus === 'start') {
this.addRemoveAlert = {
type: 'info',
text: `${this.addRemoveAction} started: ${s}s remaining`
}
}
if (now > newVal) clearInterval(this.addRemoveTimer)
}, 100)
},
controllerStatus (status) {
if (status.indexOf('clusion') > 0) {
if (this.addRemoveAction === null) return // ignore initial status
// inclusion/exclusion started, start the countdown timer
if (status.indexOf('started') > 0) {
this.addRemoveEndDate = new Date(
new Date().getTime() + this.timeoutMs
)
this.addRemoveNode = null
this.addRemoveStatus = 'start'
} else if (status.indexOf('stopped') > 0) {
// inclusion/exclusion stopped, check what happened
this.addRemoveEndDate = new Date()
this.addRemoveAlert = {
type: 'info',
text: `${this.addRemoveAction} stopped, checking nodes…`
}
this.addRemoveStatus = 'wait'
this.waitTimeout = setTimeout(this.showResults, 5000) // add additional discovery time
} else {
// error
this.addRemoveEndDate = new Date()
this.addRemoveAlert = {
type: 'error',
text: status // TODO: better formatting?
}
this.addRemoveStatus = 'stop'
}
}
}
},
watch: {},
data () {
return {
settings: new Settings(localStorage),
bindedSocketEvents: {}, // keep track of the events-handlers
addRemoveShowDialog: false,
addRemoveAction: null,
addRemoveStatus: 'stop',
waitTimeout: null,
addRemoveAlert: null,
addRemoveEndDate: new Date(),
addRemoveTimer: null,
addRemoveNode: null,
node_actions: [
{
Expand Down Expand Up @@ -268,52 +213,8 @@ export default {
},
onAddRemoveClose () {
this.addRemoveShowDialog = false
this.addRemoveAlert = null
},
async onAddRemoveAction (action) {
this.addRemoveStatus = 'wait' // make sure user can't trigger another action too soon
this.addRemoveAction = action.name // Inclusion/Secure inclusion/Exclusion
this.addRemoveEndDate = new Date()
this.addRemoveAlert = {
type: 'info',
text: `${action.name} ${
action.method === 'start' ? 'starting…' : 'stopping…'
}`
}
const args = []
if (action.secure && action.id < 2 && action.method === 'start') {
args.push(action.secure)
}
this.apiRequest(action.method + action.baseAction, args)
this.addRemoveNode = null
},
showResults () {
if (this.waitTimeout) {
clearTimeout(this.waitTimeout)
this.waitTimeout = null
}
this.addRemoveAlert = null
if (this.addRemoveNode == null) {
this.addRemoveAlert = {
type: 'warning',
text: `${this.addRemoveAction} stopped, none found`
}
} else if (this.addRemoveAction === 'Exclusion') {
this.addRemoveAlert = {
type: 'success',
text: `Node ${this.addRemoveNode.id} removed`
}
} else {
this.addRemoveAlert = {
type: 'success',
text: `Device found! Node ${this.addRemoveNode.id} added` // we don't know yet if it's added securely or not, need to wait interview
}
}
this.addRemoveStatus = 'stop'
},
async sendCntAction () {
if (this.cnt_action) {
const args = []
Expand Down Expand Up @@ -445,10 +346,6 @@ export default {
},
onNodeAddedRemoved (node) {
this.addRemoveNode = node
// the add/remove dialog is waiting for a feedback
if (this.waitTimeout) {
this.showResults()
}
},
bindEvent (eventName, handler) {
this.socket.on(socketEvents[eventName], handler)
Expand All @@ -472,13 +369,6 @@ export default {
if (this.socket) {
this.unbindEvents()
}
if (this.addRemoveTimer) {
clearInterval(this.addRemoveTimer)
}
if (this.waitTimeout) {
clearTimeout(this.waitTimeout)
}
}
}
</script>
171 changes: 144 additions & 27 deletions src/components/dialogs/DialogAddRemove.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

<v-card-text style="padding-bottom:0">
<v-container fluid style="margin-top:-2rem">
<v-radio-group v-model="mode" mandatory>
<v-radio value="0">
<v-radio-group v-model="selectedMode" mandatory>
<v-radio :value="0">
<template v-slot:label>
<div class="option">
<v-icon color="green accent-4" small>add_circle</v-icon>
Expand All @@ -19,7 +19,7 @@
</div>
</template>
</v-radio>
<v-radio value="1">
<v-radio :value="1">
<template v-slot:label>
<div class="option">
<v-icon color="amber accent-4" small
Expand All @@ -30,7 +30,7 @@
</div>
</template>
</v-radio>
<v-radio value="2">
<v-radio :value="2">
<template v-slot:label>
<div class="option">
<v-icon color="red accent-4" small>remove_circle</v-icon>
Expand All @@ -50,17 +50,17 @@
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
v-if="status === 'stop'"
v-if="state === 'stop' || state === 'new'"
color="red darken-1"
text
@click="$emit('close')"
>Close</v-btn
>
<v-btn
v-if="status !== 'wait'"
v-if="state !== 'wait'"
color="blue darken-1"
text
@click="$emit('action', action)"
@click="onAction"
>{{ method }}</v-btn
>
</v-card-actions>
Expand All @@ -69,51 +69,168 @@
</template>

<script>
import { mapGetters } from 'vuex'
export default {
components: {},
props: {
value: Boolean, // show or hide
status: String,
alert: Object
},
watch: {},
computed: {
method () {
return this.status === 'stop' ? 'start' : 'stop'
},
action () {
return {
...this.modes[this.mode],
method: this.method
}
}
nodeAddedOrRemoved: Object
},
data () {
return {
selectedMode: 0,
mode: 0, // most common action should be default
modes: [
{
id: 0,
baseAction: 'Inclusion',
name: 'Inclusion',
secure: false
},
{
id: 1,
baseAction: 'Inclusion',
name: 'Secure inclusion',
secure: true
},
{
id: 2,
baseAction: 'Exclusion',
name: 'Exclusion',
secure: false
}
]
],
state: 'new', // new -> wait -> start -> wait -> stop
commandEndDate: new Date(),
commandTimer: null,
waitTimeout: null,
alert: null,
nodeFound: null
}
},
computed: {
...mapGetters(['appInfo', 'zwave']),
method () {
return this.state === 'new' || this.state === 'stop' ? 'start' : 'stop'
},
timeoutMs () {
return this.zwave.commandsTimeout * 1000 + 800 // add small buffer
},
controllerStatus () {
return this.appInfo.controllerStatus
},
modeName () {
return this.modes[this.mode].name
}
},
watch: {
nodeAddedOrRemoved (node) {
this.nodeFound = node
// the add/remove dialog is waiting for a feedback
if (this.waitTimeout) {
this.showResults()
}
},
commandEndDate (newVal) {
if (this.commandTimer) {
clearInterval(this.commandTimer)
}
this.commandTimer = setInterval(() => {
const now = new Date()
const s = Math.trunc((this.commandEndDate - now) / 1000)
if (this.state === 'start') {
this.alert = {
type: 'info',
text: `${this.modeName} started: ${s}s remaining`
}
}
if (now > newVal) clearInterval(this.commandTimer)
}, 100)
},
controllerStatus (status) {
if (status.indexOf('clusion') > 0) {
if (this.state === 'new') return // ignore initial status
// inclusion/exclusion started, start the countdown timer
if (status.indexOf('started') > 0) {
this.commandEndDate = new Date(new Date().getTime() + this.timeoutMs)
this.nodeFound = null
this.state = 'start'
} else if (status.indexOf('stopped') > 0) {
// inclusion/exclusion stopped, check what happened
this.commandEndDate = new Date()
this.alert = {
type: 'info',
text: `${this.modeName} stopped, checking nodes…`
}
this.state = 'wait'
this.waitTimeout = setTimeout(this.showResults, 5000) // add additional discovery time
} else {
// error
this.commandEndDate = new Date()
this.alert = {
type: 'error',
text: status // TODO: better formatting?
}
this.state = 'stop'
}
}
}
},
methods: {
onAction () {
this.mode = this.selectedMode
this.commandEndDate = new Date()
this.alert = {
type: 'info',
text: `${this.modeName} ${
this.method === 'start' ? 'starting…' : 'stopping…'
}`
}
const args = []
if (this.mode < 2 && this.method === 'start') {
args.push(this.modes[this.mode].secure)
}
this.$emit(
'apiRequest',
this.method + this.modes[this.mode].baseAction,
args
)
this.state = 'wait' // make sure user can't trigger another action too soon
},
showResults () {
if (this.waitTimeout) {
clearTimeout(this.waitTimeout)
this.waitTimeout = null
}
if (this.nodeFound === null) {
this.alert = {
type: 'warning',
text: `${this.modeName} stopped, no changes detected`
}
} else if (this.mode === 2) {
this.alert = {
type: 'success',
text: `Node ${this.nodeFound.id} removed`
}
} else {
this.alert = {
type: 'success',
text: `Device found! Node ${this.nodeFound.id} added` // we don't know yet if it's added securely or not, need to wait interview
}
}
this.state = 'stop'
}
},
methods: {}
beforeDestroy () {
if (this.commandTimer) {
clearInterval(this.commandTimer)
}
if (this.waitTimeout) {
clearTimeout(this.waitTimeout)
}
}
}
</script>

Expand Down

0 comments on commit 0ecbea6

Please sign in to comment.