smart-home
Version:
Netbeast dashboard, IoT apps manager
179 lines (150 loc) • 5.82 kB
JSX
/* global toastr */
import React from 'react'
import mqtt from 'mqtt'
import request from 'superagent-bluebird-promise'
import { OverlayTrigger, Popover } from 'react-bootstrap'
import Pulse from '../misc/activity-pulse.jsx'
import { Session } from '../lib'
export default class App extends React.Component {
constructor (props, context) {
super(props)
this.state = { isRunning: false, inactive: false, menuHidden: true }
this.router = context.router
}
handleClick () {
const { type } = this.props
if (type !== 'explore') this.launch()
}
toggleMenu (event) {
if (event) event.preventDefault() && event.stopPropagation()
const { menuHidden } = this.state
if (menuHidden) this.refs.contextMenu.show()
else this.refs.contextMenu.hide()
this.setState({ menuHidden: !menuHidden })
}
launch () {
const { name } = this.props
request.post('/api/activities/' + name).then(() => {
return request.get('/i/' + name).promise()
}).then(() => {
this.router.push('/live/' + name)
}).catch((err) => {
if (err.status === 404) {
this.setState({ isRunning: true })
return toastr.info(`${name} is running`)
}
if (err.res) toastr.error(err.res.text)
else toastr.error(err.message)
})
}
install () {
const url = this.props.git_url
const loader = window.notify({
body: (
<span>
<div className='loader'></div>
Installing app...
</span>
), timeout: 0}
)
request.post('/api/apps').send({ url }).then((res) => {
const name = res.body.name
const props = res.body.netbeast
const type = props ? props.type : 'app'
toastr.success(`${name} has been installed!`)
toastr.dismiss(loader)
if (type === 'plugin' || type === 'service' || props.bootOnLoad)
return request.post('/api/activities/' + name).promise()
}).then((res) => { toastr.success(`${res.body.name} is running`) })
.catch((fail, res) => toastr.error(res.text))
}
stop () {
const { name, kind, dismiss } = this.props
request.del('/api/activities/' + name).end((err, res) => {
if (err) return
this.setState({ isRunning: false })
toastr.info(name + ' has been stopped.')
if (kind !== 'apps' && kind !== 'plugins') dismiss(name)
})
}
uninstall () {
const { name, kind, dismiss } = this.props
if (!confirm('Do you really want to remove', name, '?')) return
const loader = window.notify({
body: (
<span>
<div className='loader'></div>
Uninstalling {name}...
</span>
), timeout: 0}
)
request.del('/api/apps/' + name).end((err, res) => {
if (err) return
dismiss(name)
toastr.dismiss(loader)
toastr.info(name + ' has been removed.')
})
}
contextMenu () {
const { name, netbeast } = this.props
const settings = netbeast && netbeast.settings
return (
<Popover id={name} className='context-menu'>
<a href='javascript:void(0)' onClick={this.stop.bind(this)} className='stop btn btn-filled btn-warning'> Stop </a>
<a href='javascript:void(0)' onClick={this.uninstall.bind(this)} className='remove btn btn-filled btn-primary'> Remove </a>
{settings ? <a onClick={this.router.push.bind(this, '/i/' + name + ((settings === true && typeof settings === 'boolean') ? '/settings' : settings))} className='settings btn btn-filled btn-success'> Settings </a> : null}
</Popover>
)
}
renderButton () {
const { kind, git_url } = this.props
switch (kind) {
case 'activities':
return <a href='javascript:void(0)' onClick={this.stop.bind(this)} className='stop btn btn-filled btn-warning'> Stop </a>
case 'remove':
return <a href='javascript:void(0)' onClick={this.uninstall.bind(this)} className='remove btn btn-filled btn-primary'> Remove </a>
case 'explore':
return <a href='javascript:void(0)' onClick={API.install.bind(API, git_url)} className='install btn btn-filled btn-info'> Install </a>
}
}
componentDidMount () {
const { name, netbeast } = this.props
this.mqtt = mqtt.connect(window.mqttUri)
this.mqtt.subscribe('netbeast/activities/close')
this.mqtt.on('message', (topic, message) => {
if (message.toString() === name) this.setState({ isRunning: false })
})
request.get('/api/activities/' + name).end((err, res) => {
if (!err && res.body.port > 0) this.setState({ isRunning: true })
})
if (netbeast && (netbeast.type === 'plugin')) {
const devices = Session.load('devices') || []
const found = devices.find((d) => { return d.app === name })
this.setState({ inactive: !found })
}
}
componentWillUnmount () {
this.mqtt.end()
}
render () {
const { name, author, logo, netbeast } = this.props
const isPlugin = netbeast && (netbeast.type === 'plugin')
const defaultLogo = isPlugin ? 'url(/img/plugin.png)' : 'url(/img/dflt.png)'
const logoStyle = { backgroundImage: logo ? `url(/api/apps/${name}/logo)` : defaultLogo }
const { inactive, isRunning } = this.state
const inactiveClass = inactive && isRunning ? ' warning' : ''
return (
<div className={'app' + inactiveClass}>
{(isRunning) ? <Pulse {...this.props} /> : null}
<OverlayTrigger ref='contextMenu' trigger={[]} rootClose placement='bottom' overlay={this.contextMenu()}>
<div className='logo' title='Launch app' style={logoStyle} onClick={this.handleClick.bind(this)} onContextMenu={this.toggleMenu.bind(this)} />
</OverlayTrigger>
{this.renderButton()}
<p className='name'>{name}</p>
</div>
)
}
}
App.contextTypes = {
router: React.PropTypes.object.isRequired
}