mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-30 06:13:10 +01:00
Convert to ES5, add web server
* No compilation step * Beginnings of web interface * Simple express server; React-based frontend * CommonJS style across codebase; auto-converts to RequireJS for browser * Using diffsync for realtime UI * "Provider" -> "Plugin" * Plugins expose one or more Providers
This commit is contained in:
16
public/js/admin.jsx
Normal file
16
public/js/admin.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
module.exports.Admin = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="container">
|
||||
<h2>Installed Plugins</h2>
|
||||
<ul>
|
||||
{ this.props.root.plugins.map(function(plugin) {
|
||||
return <li key={plugin.name}>{plugin.name}</li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
116
public/js/app.jsx
Normal file
116
public/js/app.jsx
Normal file
@@ -0,0 +1,116 @@
|
||||
var Route = ReactRouter.Route;
|
||||
var DefaultRoute = ReactRouter.DefaultRoute;
|
||||
var NotFoundRoute = ReactRouter.NotFoundRoute;
|
||||
var RouteHandler = ReactRouter.RouteHandler;
|
||||
var Link = ReactRouter.Link;
|
||||
var ProviderGrid = require('./providers.jsx').ProviderGrid;
|
||||
var Admin = require('./admin.jsx').Admin;
|
||||
var NotificationCenter = require('./notifications.jsx').NotificationCenter;
|
||||
|
||||
var App = React.createClass({
|
||||
getInitialState: function() {
|
||||
return { root: null };
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
|
||||
// pass the connection and the id of the data you want to synchronize
|
||||
var client = new diffsync.Client(io(), "root");
|
||||
|
||||
client.on('connected', function(){
|
||||
// initial data was loaded - pass it on to our state
|
||||
this.setState({ root: client.getData() });
|
||||
|
||||
// if we're using browser-refresh to auto-reload the browser during development, then
|
||||
// we'll receive the URL to a JS script in this location (see Server.js)
|
||||
if (this.state.root.browserRefreshURL) {
|
||||
var script = document.createElement('script');
|
||||
script.setAttribute('src', this.state.root.browserRefreshURL);
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
|
||||
client.on('synced', function(){
|
||||
// server has updated our data - pass it on to our state
|
||||
this.setState({ root: client.getData() });
|
||||
|
||||
}.bind(this));
|
||||
|
||||
client.initialize();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var root = this.state.root;
|
||||
|
||||
return root && (
|
||||
<div>
|
||||
<nav className="navbar navbar-default navbar-static-top">
|
||||
<div className="container">
|
||||
<div className="navbar-header">
|
||||
<Link className="navbar-brand" to="/" style={{opacity:root.serverChange}}>homebridge</Link>
|
||||
</div>
|
||||
<div id="navbar" className="navbar-collapse collapse">
|
||||
<ul className="nav navbar-nav navbar-right">
|
||||
|
||||
{/* Notification Center dropdown */}
|
||||
<li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<span className="glyphicon glyphicon-bell" aria-hidden="true"></span>
|
||||
</a>
|
||||
<div className="dropdown-menu" style={{padding:"10px"}}>
|
||||
<NotificationCenter notifications={root.notifications}/>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
{/* Settings dropdown */}
|
||||
<li className="dropdown">
|
||||
<a href="#" className="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||
<span className="glyphicon glyphicon-cog" aria-hidden="true"></span>
|
||||
</a>
|
||||
<ul className="dropdown-menu">
|
||||
<li><Link to="/admin">Admin</Link></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<RouteHandler root={root}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var Home = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="container">
|
||||
<ProviderGrid root={this.props.root}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var NotFound = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<div className="container" style={{textAlign:"center",marginTop:"100px"}}>
|
||||
<h1>That page could not be found.</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
var routes = (
|
||||
<Route path="/" handler={App}>
|
||||
<DefaultRoute handler={Home}/>
|
||||
<Route name="admin" handler={Admin}/>
|
||||
<NotFoundRoute handler={NotFound}/>
|
||||
</Route>
|
||||
);
|
||||
|
||||
ReactRouter.run(routes, ReactRouter.HistoryLocation, function (Handler) {
|
||||
React.render(<Handler/>, document.body);
|
||||
});
|
||||
19
public/js/notifications.jsx
Normal file
19
public/js/notifications.jsx
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
module.exports.NotificationCenter = React.createClass({
|
||||
render: function() {
|
||||
|
||||
var notifications = this.props.notifications;
|
||||
|
||||
if (!notifications || notifications.length == 0) return (
|
||||
<div style={{color:"#999"}}>No Notifications</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ notifications.map(function(notification, index) {
|
||||
return <div key={index}>{notification.message}</div>;
|
||||
}) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
218
public/js/providers.jsx
Normal file
218
public/js/providers.jsx
Normal file
@@ -0,0 +1,218 @@
|
||||
var Link = ReactRouter.Link;
|
||||
|
||||
/**
|
||||
* Provider Grid - displays all created providers.
|
||||
*/
|
||||
|
||||
module.exports.ProviderGrid = React.createClass({
|
||||
|
||||
render() {
|
||||
var root = this.props.root;
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
|
||||
{/* need a wrapper div to counteract card margins around the conatiner edges */}
|
||||
<div style={Styles.cardsWrapper}>
|
||||
{root.providers.map(function(provider) {
|
||||
<ProviderCard provider={provider} key={provider.key}/>
|
||||
})}
|
||||
<ProviderCard/>
|
||||
<ProviderCard/>
|
||||
</div>
|
||||
|
||||
{/* add provider */}
|
||||
<AddProviderButton plugins={root.plugins}/>
|
||||
|
||||
</div>
|
||||
)
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Provider "Card"
|
||||
*/
|
||||
|
||||
var ProviderCard = React.createClass({
|
||||
render() {
|
||||
var provider = this.props.provider;
|
||||
|
||||
var imageStyle = {
|
||||
background: "url(//pbs.twimg.com/profile_images/519977105543528448/HAc6jtgo_400x400.png)",
|
||||
backgroundSize: "cover",
|
||||
height: "100%"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="panel panel-default" style={Styles.card} onClick={this.cardClicked}>
|
||||
<div className="panel-body" style={Styles.cardBody}>
|
||||
<div style={imageStyle}></div>
|
||||
</div>
|
||||
<div className="panel-footer" style={Styles.cardFooter}>
|
||||
WeMo
|
||||
<span style={Styles.cardTimestamp}>
|
||||
5 accessories
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
cardClicked() {
|
||||
console.log("Click!");
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add Provider button + dialog
|
||||
*/
|
||||
|
||||
var AddProviderButton = React.createClass({
|
||||
getInitialState() {
|
||||
return {
|
||||
selectedPlugin: null,
|
||||
selectedProvider: null,
|
||||
newProviderName: null
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
var plugins = this.props.plugins;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<div style={Styles.addProvider}>
|
||||
<button type="button" className="btn btn-primary btn-lg" data-toggle="modal" data-target="#addProviderModal">
|
||||
Add Provider
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="modal fade" id="addProviderModal" tabIndex="-1" role="dialog">
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal"><span>×</span></button>
|
||||
<h4 className="modal-title">Add New Provider</h4>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
|
||||
<form className="form-horizontal">
|
||||
<div className="form-group">
|
||||
<label htmlFor="inputEmail3" className="col-sm-2 control-label">Provider</label>
|
||||
<div className="col-sm-5">
|
||||
<ProvidersDropdown plugins={plugins} selectedProvider={this.state.selectedProvider} onSelectProvider={this.onSelectProvider}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label htmlFor="inputPassword3" className="col-sm-2 control-label">Name</label>
|
||||
<div className="col-sm-5">
|
||||
<input type="text" className="form-control" id="inputPassword3" placeholder={
|
||||
(this.state.selectedProvider && this.state.selectedProvider.title) || null
|
||||
}/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
<button type="button" className="btn btn-primary">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
onSelectProvider(plugin, provider) {
|
||||
this.setState({
|
||||
selectedPlugin: plugin,
|
||||
selectedProvider: provider
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Providers Dropdown
|
||||
*/
|
||||
|
||||
var ProvidersDropdown = React.createClass({
|
||||
render() {
|
||||
var plugins = this.props.plugins;
|
||||
|
||||
var items = [];
|
||||
|
||||
plugins.forEach(function(plugin) {
|
||||
items.push(<li key={plugin.name} className="dropdown-header">{plugin.name}</li>);
|
||||
|
||||
plugin.providers.forEach(function(provider) {
|
||||
items.push(
|
||||
<li key={'provider-'+provider.name}>
|
||||
<a href="#" onClick={function() { this.onSelectProvider(plugin, provider); }.bind(this) }>
|
||||
{provider.title}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this));
|
||||
|
||||
return (
|
||||
<div className="dropdown">
|
||||
<button className="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
{ (this.props.selectedProvider && this.props.selectedProvider.title) || 'Select Provider' }
|
||||
|
||||
<span className="caret"></span>
|
||||
</button>
|
||||
<ul className="dropdown-menu">
|
||||
{ items }
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
onSelectProvider(plugin, provider) {
|
||||
if (this.props.onSelectProvider)
|
||||
this.props.onSelectProvider(plugin, provider);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* CSS Styles
|
||||
*/
|
||||
|
||||
var Styles = {
|
||||
cardsWrapper: {
|
||||
margin: "-10px"
|
||||
},
|
||||
card: {
|
||||
width: "150px",
|
||||
display: "inline-block",
|
||||
margin: "10px",
|
||||
cursor: "pointer"
|
||||
},
|
||||
cardBody: {
|
||||
height: "150px",
|
||||
padding: "0"
|
||||
},
|
||||
cardFooter: {
|
||||
fontSize:"100%",
|
||||
fontWeight:"bold",
|
||||
textAlign: "center",
|
||||
background: "#fafafa"
|
||||
},
|
||||
cardTimestamp: {
|
||||
fontSize: "80%",
|
||||
fontWeight: "lighter",
|
||||
display: "block",
|
||||
},
|
||||
addProvider: {
|
||||
margin: "30px",
|
||||
textAlign: "center"
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user