Removing active event from ContainerStore

This commit is contained in:
Jeffrey Morgan 2015-01-21 20:48:03 -05:00
parent 63062399e3
commit 72ccbc6c35
5 changed files with 136 additions and 55 deletions

View File

@ -1,15 +1,19 @@
var _ = require('underscore'); var _ = require('underscore');
var React = require('react'); var React = require('react');
var Router = require('react-router'); var Router = require('react-router');
var Convert = require('ansi-to-html');
var convert = new Convert();
var ContainerStore = require('./ContainerStore');
var docker = require('./docker');
var exec = require('exec');
var boot2docker = require('./boot2docker');
var ProgressBar = require('react-bootstrap/ProgressBar');
var Route = Router.Route; var Route = Router.Route;
var NotFoundRoute = Router.NotFoundRoute; var NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute; var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link; var Link = Router.Link;
var RouteHandler = Router.RouteHandler; var RouteHandler = Router.RouteHandler;
var Convert = require('ansi-to-html');
var convert = new Convert();
var ContainerStore = require('./ContainerStore');
var docker = require('./docker');
var ContainerDetails = React.createClass({ var ContainerDetails = React.createClass({
mixins: [Router.State], mixins: [Router.State],
@ -19,14 +23,17 @@ var ContainerDetails = React.createClass({
}; };
}, },
componentWillReceiveProps: function () { componentWillReceiveProps: function () {
console.log('props');
this.update(); this.update();
this.setState({
logs: []
});
var self = this; var self = this;
var logs = []; var logs = [];
var index = 0; var index = 0;
docker.client().getContainer(this.getParams().name).logs({ docker.client().getContainer(this.getParams().name).logs({
follow: false, follow: false,
stdout: true, stdout: true,
stderr: true,
timestamps: true timestamps: true
}, function (err, stream) { }, function (err, stream) {
stream.setEncoding('utf8'); stream.setEncoding('utf8');
@ -44,6 +51,7 @@ var ContainerDetails = React.createClass({
docker.client().getContainer(self.getParams().name).logs({ docker.client().getContainer(self.getParams().name).logs({
follow: true, follow: true,
stdout: true, stdout: true,
stderr: true,
timestamps: true, timestamps: true,
tail: 0 tail: 0
}, function (err, stream) { }, function (err, stream) {
@ -75,11 +83,9 @@ var ContainerDetails = React.createClass({
}, },
update: function () { update: function () {
var name = this.getParams().name; var name = this.getParams().name;
var container = ContainerStore.container(name);
var progress = ContainerStore.progress(name);
this.setState({ this.setState({
progress: progress, container: ContainerStore.container(name),
container: container progress: ContainerStore.progress(name)
}); });
}, },
_escapeHTML: function (html) { _escapeHTML: function (html) {
@ -88,8 +94,32 @@ var ContainerDetails = React.createClass({
div.appendChild(text); div.appendChild(text);
return div.innerHTML; return div.innerHTML;
}, },
handleClick: function (name) {
var container = this.state.container;
boot2docker.ip(function (err, ip) {
var ports = _.map(container.NetworkSettings.Ports, function (value, key) {
var portProtocolPair = key.split('/');
var res = {
'port': portProtocolPair[0],
'protocol': portProtocolPair[1]
};
if (value && value.length) {
var port = value[0].HostPort;
res.host = ip;
res.port = port;
res.url = 'http://' + ip + ':' + port;
} else {
return null;
}
return res;
});
console.log(ports);
exec(['open', ports[0].url], function (err) {
if (err) { throw err; }
});
});
},
render: function () { render: function () {
console.log('render details');
var self = this; var self = this;
if (!this.state) { if (!this.state) {
@ -111,12 +141,32 @@ var ContainerDetails = React.createClass({
state = <h2 className="status">restarting</h2>; state = <h2 className="status">restarting</h2>;
} }
var progress;
if (this.state.progress > 0 && this.state.progress != 1) {
progress = (
<div className="details-progress">
<ProgressBar now={this.state.progress * 100} label="%(percent)s%" />
</div>
);
} else {
progress = <div></div>;
}
var button;
if (this.state.progress === 1) {
button = <a className="btn btn-primary" onClick={this.handleClick}>View</a>;
} else {
button = <a className="btn btn-primary disabled" onClick={this.handleClick}>View</a>;
}
var name = this.state.container.Name.replace('/', '');
return ( return (
<div className="details"> <div className="details">
<div className="details-header"> <div className="details-header">
<h1>{this.state.container.Name.replace('/', '')}</h1> <h1>{name}</h1> <a className="btn btn-primary" onClick={this.handleClick}>View</a>
<h2>{this.state.progress}</h2>
</div> </div>
{progress}
<div className="details-logs"> <div className="details-logs">
<div className="logs"> <div className="logs">
{logs} {logs}

View File

@ -16,16 +16,13 @@ var RouteHandler = Router.RouteHandler;
var Navigation= Router.Navigation; var Navigation= Router.Navigation;
var ContainerList = React.createClass({ var ContainerList = React.createClass({
mixins: [Navigation],
getInitialState: function () { getInitialState: function () {
return { return {
active: null,
containers: [] containers: []
}; };
}, },
componentDidMount: function () { componentDidMount: function () {
this.updateContainers(); this.updateContainers();
ContainerStore.addChangeListener(ContainerStore.ACTIVE, this.updateActive);
ContainerStore.addChangeListener(ContainerStore.CONTAINERS, this.updateContainers); ContainerStore.addChangeListener(ContainerStore.CONTAINERS, this.updateContainers);
}, },
componentWillMount: function () { componentWillMount: function () {
@ -33,29 +30,13 @@ var ContainerList = React.createClass({
}, },
componentWillUnmount: function () { componentWillUnmount: function () {
ContainerStore.removeChangeListener(ContainerStore.CONTAINERS, this.updateContainers); ContainerStore.removeChangeListener(ContainerStore.CONTAINERS, this.updateContainers);
ContainerStore.removeChangeListener(ContainerStore.ACTIVE, updateActive.update);
},
updateActive: function () {
if (ContainerStore.active()) {
this.transitionTo('container', {name: ContainerStore.active()});
}
}, },
updateContainers: function () { updateContainers: function () {
// Sort by name // Sort by name
var containers = _.values(ContainerStore.containers()).sort(function (a, b) { var containers = _.values(ContainerStore.containers()).sort(function (a, b) {
return a.Name.localeCompare(b.Name); return a.Name.localeCompare(b.Name);
}); });
this.setState({containers: containers}); this.setState({containers: containers});
// Transition to the active container or set one
var active = ContainerStore.active();
if (!ContainerStore.container(active) && containers.length > 0) {
ContainerStore.setActive(containers[0].Name.replace('/', ''));
}
},
handleClick: function (containerId) {
ContainerStore.setActive(name);
}, },
render: function () { render: function () {
var self = this; var self = this;
@ -95,13 +76,15 @@ var ContainerList = React.createClass({
state = <div className="state state-stopped"></div>; state = <div className="state state-stopped"></div>;
} }
var name = container.Name.replace('/', '');
return ( return (
<Link key={container.Name.replace('/', '')} to="container" params={{name: container.Name.replace('/', '')}} onClick={self.handleClick.bind(self, container.Id)}> <Link key={name} data-container={name} to="container" params={{name: name}}>
<li> <li>
{state} {state}
<div className="info"> <div className="info">
<div className="name"> <div className="name">
{container.Name.replace('/', '')} {name}
</div> </div>
<div className="image"> <div className="image">
{imageName} {imageName}

View File

@ -1,18 +1,21 @@
var async = require('async');
var $ = require('jquery');
var React = require('react'); var React = require('react');
var Router = require('react-router'); var Router = require('react-router');
var Modal = require('react-bootstrap/Modal'); var Modal = require('react-bootstrap/Modal');
var RetinaImage = require('react-retina-image'); var RetinaImage = require('react-retina-image');
var $ = require('jquery');
var ContainerStore = require('./ContainerStore'); var ContainerStore = require('./ContainerStore');
var Navigation = Router.Navigation; var Navigation = Router.Navigation;
var ContainerModal = React.createClass({ var ContainerModal = React.createClass({
mixins: [Navigation],
_searchRequest: null, _searchRequest: null,
getInitialState: function () { getInitialState: function () {
return { return {
query: '', query: '',
results: [] results: [],
recommended: ContainerStore.recommended()
}; };
}, },
componentDidMount: function () { componentDidMount: function () {
@ -21,6 +24,8 @@ var ContainerModal = React.createClass({
search: function (query) { search: function (query) {
var self = this; var self = this;
this._searchRequest = $.get('https://registry.hub.docker.com/v1/search?q=' + query, function (result) { this._searchRequest = $.get('https://registry.hub.docker.com/v1/search?q=' + query, function (result) {
self._searchRequest.abort();
self._searchRequest = null;
if (self.isMounted()) { if (self.isMounted()) {
self.setState(result); self.setState(result);
console.log(result); console.log(result);
@ -35,6 +40,7 @@ var ContainerModal = React.createClass({
} }
if (this._searchRequest) { if (this._searchRequest) {
console.log('Cancel');
this._searchRequest.abort(); this._searchRequest.abort();
this._searchRequest = null; this._searchRequest = null;
} }
@ -48,14 +54,20 @@ var ContainerModal = React.createClass({
var name = event.target.getAttribute('name'); var name = event.target.getAttribute('name');
var self = this; var self = this;
ContainerStore.create(name, 'latest', function (err, containerName) { ContainerStore.create(name, 'latest', function (err, containerName) {
ContainerStore.setActive(containerName); // this.transitionTo('containers', {container: containerName});
self.props.onRequestHide(); self.props.onRequestHide();
}); }.bind(this));
}, },
render: function () { render: function () {
var top = this.state.results.splice(0, 7);
var self = this; var self = this;
var results = top.map(function (r) {
var data;
if (this.state.query) {
data = this.state.results.splice(0, 7);
} else {
data = this.state.recommended;
}
var results = data.map(function (r) {
var name; var name;
if (r.is_official) { if (r.is_official) {
name = <span><RetinaImage src="official.png"/>{r.name}</span>; name = <span><RetinaImage src="official.png"/>{r.name}</span>;
@ -79,6 +91,14 @@ var ContainerModal = React.createClass({
</li> </li>
); );
}); });
var title;
if (this.state.query) {
title = <div className="title">Results</div>;
} else {
title = <div className="title">Recommended</div>;
}
return ( return (
<Modal {...this.props} animation={false} className="create-modal"> <Modal {...this.props} animation={false} className="create-modal">
<div className="modal-body"> <div className="modal-body">
@ -88,7 +108,7 @@ var ContainerModal = React.createClass({
<a href="#"><span>What&#39;s an image?</span></a> <a href="#"><span>What&#39;s an image?</span></a>
</div> </div>
<div className="results"> <div className="results">
<div className="title">Results</div> {title}
<ul> <ul>
{results} {results}
</ul> </ul>

View File

@ -11,11 +11,11 @@ var ContainerStore = assign(EventEmitter.prototype, {
CONTAINERS: 'containers', CONTAINERS: 'containers',
PROGRESS: 'progress', PROGRESS: 'progress',
LOGS: 'logs', LOGS: 'logs',
ACTIVE: 'active', RECOMMENDED: 'recommended',
_recommended: [],
_containers: {}, _containers: {},
_progress: {}, _progress: {},
_logs: {}, _logs: {},
_active: null,
_pullScratchImage: function (callback) { _pullScratchImage: function (callback) {
var image = docker.client().getImage('scratch:latest'); var image = docker.client().getImage('scratch:latest');
image.inspect(function (err, data) { image.inspect(function (err, data) {
@ -101,7 +101,9 @@ var ContainerStore = assign(EventEmitter.prototype, {
// Refresh with docker & hook into events // Refresh with docker & hook into events
var self = this; var self = this;
this.update(function (err) { this.update(function (err) {
callback(); self.updateRecommended(function (err) {
callback();
});
var downloading = _.filter(_.values(self._containers), function (container) { var downloading = _.filter(_.values(self._containers), function (container) {
var env = container.Config.Env; var env = container.Config.Env;
return _.indexOf(env, 'KITEMATIC_DOWNLOADING=true') !== -1; return _.indexOf(env, 'KITEMATIC_DOWNLOADING=true') !== -1;
@ -168,6 +170,30 @@ var ContainerStore = assign(EventEmitter.prototype, {
}); });
}); });
}, },
updateRecommended: function (callback) {
var self = this;
$.ajax({
url: 'https://kitematic.com/recommended.json',
dataType: 'json',
success: function (res, status) {
var recommended = res.recommended;
async.map(recommended, function (repository, callback) {
$.get('https://registry.hub.docker.com/v1/search?q=' + repository, function (data) {
var results = data.results;
callback(null, _.find(results, function (r) {
return r.name === repository;
}));
});
}, function (err, results) {
self._recommended = results;
callback();
});
},
error: function (err) {
console.log(err);
}
});
},
create: function (repository, tag, callback) { create: function (repository, tag, callback) {
tag = tag || 'latest'; tag = tag || 'latest';
var self = this; var self = this;
@ -213,7 +239,6 @@ var ContainerStore = assign(EventEmitter.prototype, {
}, {}); }, {});
self._progress[containerName] = 0; self._progress[containerName] = 0;
self.emit(containerName);
stream.on('data', function (str) { stream.on('data', function (str) {
console.log(str); console.log(str);
@ -253,26 +278,19 @@ var ContainerStore = assign(EventEmitter.prototype, {
// If not then directly create the container // If not then directly create the container
self._createContainer(imageName, containerName, function () { self._createContainer(imageName, containerName, function () {
callback(null, containerName); callback(null, containerName);
console.log('done');
}); });
} }
}); });
}, },
setActive: function (name) {
console.log('set active');
this._active = name;
this.emit(this.ACTIVE);
},
active: function () {
return this._active;
},
// Returns all containers
containers: function() { containers: function() {
return this._containers; return this._containers;
}, },
container: function (name) { container: function (name) {
return this._containers[name]; return this._containers[name];
}, },
recommended: function () {
return this._recommended;
},
progress: function (name) { progress: function (name) {
return this._progress[name]; return this._progress[name];
}, },

View File

@ -233,7 +233,12 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 0px 45px 14px; padding: 0px 45px 14px;
background: white; position: relative;
a {
position: absolute;
right: 30px;
top: -4px;
}
h1 { h1 {
font-size: 24px; font-size: 24px;
margin: 0; margin: 0;
@ -253,6 +258,11 @@
} }
} }
.details-progress {
margin: 26% auto 0;
width: 300px;
}
.details-logs { .details-logs {
flex: 1; flex: 1;
overflow: auto; overflow: auto;