Removing active event from ContainerStore

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

View File

@ -1,15 +1,19 @@
var _ = require('underscore');
var React = require('react');
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 NotFoundRoute = Router.NotFoundRoute;
var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link;
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({
mixins: [Router.State],
@ -19,14 +23,17 @@ var ContainerDetails = React.createClass({
};
},
componentWillReceiveProps: function () {
console.log('props');
this.update();
this.setState({
logs: []
});
var self = this;
var logs = [];
var index = 0;
docker.client().getContainer(this.getParams().name).logs({
follow: false,
stdout: true,
stderr: true,
timestamps: true
}, function (err, stream) {
stream.setEncoding('utf8');
@ -44,6 +51,7 @@ var ContainerDetails = React.createClass({
docker.client().getContainer(self.getParams().name).logs({
follow: true,
stdout: true,
stderr: true,
timestamps: true,
tail: 0
}, function (err, stream) {
@ -75,11 +83,9 @@ var ContainerDetails = React.createClass({
},
update: function () {
var name = this.getParams().name;
var container = ContainerStore.container(name);
var progress = ContainerStore.progress(name);
this.setState({
progress: progress,
container: container
container: ContainerStore.container(name),
progress: ContainerStore.progress(name)
});
},
_escapeHTML: function (html) {
@ -88,8 +94,32 @@ var ContainerDetails = React.createClass({
div.appendChild(text);
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 () {
console.log('render details');
var self = this;
if (!this.state) {
@ -111,12 +141,32 @@ var ContainerDetails = React.createClass({
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 (
<div className="details">
<div className="details-header">
<h1>{this.state.container.Name.replace('/', '')}</h1>
<h2>{this.state.progress}</h2>
<h1>{name}</h1> <a className="btn btn-primary" onClick={this.handleClick}>View</a>
</div>
{progress}
<div className="details-logs">
<div className="logs">
{logs}

View File

@ -16,16 +16,13 @@ var RouteHandler = Router.RouteHandler;
var Navigation= Router.Navigation;
var ContainerList = React.createClass({
mixins: [Navigation],
getInitialState: function () {
return {
active: null,
containers: []
};
},
componentDidMount: function () {
this.updateContainers();
ContainerStore.addChangeListener(ContainerStore.ACTIVE, this.updateActive);
ContainerStore.addChangeListener(ContainerStore.CONTAINERS, this.updateContainers);
},
componentWillMount: function () {
@ -33,29 +30,13 @@ var ContainerList = React.createClass({
},
componentWillUnmount: function () {
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 () {
// Sort by name
var containers = _.values(ContainerStore.containers()).sort(function (a, b) {
return a.Name.localeCompare(b.Name);
});
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 () {
var self = this;
@ -95,13 +76,15 @@ var ContainerList = React.createClass({
state = <div className="state state-stopped"></div>;
}
var name = container.Name.replace('/', '');
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>
{state}
<div className="info">
<div className="name">
{container.Name.replace('/', '')}
{name}
</div>
<div className="image">
{imageName}

View File

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

View File

@ -11,11 +11,11 @@ var ContainerStore = assign(EventEmitter.prototype, {
CONTAINERS: 'containers',
PROGRESS: 'progress',
LOGS: 'logs',
ACTIVE: 'active',
RECOMMENDED: 'recommended',
_recommended: [],
_containers: {},
_progress: {},
_logs: {},
_active: null,
_pullScratchImage: function (callback) {
var image = docker.client().getImage('scratch:latest');
image.inspect(function (err, data) {
@ -101,7 +101,9 @@ var ContainerStore = assign(EventEmitter.prototype, {
// Refresh with docker & hook into events
var self = this;
this.update(function (err) {
callback();
self.updateRecommended(function (err) {
callback();
});
var downloading = _.filter(_.values(self._containers), function (container) {
var env = container.Config.Env;
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) {
tag = tag || 'latest';
var self = this;
@ -213,7 +239,6 @@ var ContainerStore = assign(EventEmitter.prototype, {
}, {});
self._progress[containerName] = 0;
self.emit(containerName);
stream.on('data', function (str) {
console.log(str);
@ -253,26 +278,19 @@ var ContainerStore = assign(EventEmitter.prototype, {
// If not then directly create the container
self._createContainer(imageName, containerName, function () {
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() {
return this._containers;
},
container: function (name) {
return this._containers[name];
},
recommended: function () {
return this._recommended;
},
progress: function (name) {
return this._progress[name];
},

View File

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