diff --git a/src/actions/ContainerActions.js b/src/actions/ContainerActions.js index cb97435e2b..07f75167e9 100644 --- a/src/actions/ContainerActions.js +++ b/src/actions/ContainerActions.js @@ -24,10 +24,17 @@ class ContainerServerActions { } update (name, container) { - console.log(container); this.dispatch({container}); dockerUtil.updateContainer(name, container); } + + clearPending () { + this.dispatch(); + } + + run (name, repo, tag) { + dockerUtil.run(name, repo, tag); + } } export default alt.createActions(ContainerServerActions); diff --git a/src/actions/ContainerServerActions.js b/src/actions/ContainerServerActions.js index e1a88e0e71..3bc0d29ce6 100644 --- a/src/actions/ContainerServerActions.js +++ b/src/actions/ContainerServerActions.js @@ -10,6 +10,7 @@ class ContainerServerActions { 'muted', 'unmuted', 'progress', + 'pending', 'updated', 'waiting' ); diff --git a/src/components/ContainerHome.react.js b/src/components/ContainerHome.react.js index 5cbf3c0a1e..45ffec576b 100644 --- a/src/components/ContainerHome.react.js +++ b/src/components/ContainerHome.react.js @@ -5,8 +5,6 @@ var Radial = require('./Radial.react'); var ContainerHomePreview = require('./ContainerHomePreview.react'); var ContainerHomeLogs = require('./ContainerHomeLogs.react'); var ContainerHomeFolders = require('./ContainerHomeFolders.react'); -var containerUtil = require('../utils/ContainerUtil'); -var util = require ('../utils/Util'); var shell = require('shell'); var ContainerHome = React.createClass({ @@ -100,7 +98,7 @@ var ContainerHome = React.createClass({ if (_.keys(this.props.ports) > 0) { right = (
- +
); diff --git a/src/components/ContainerHomePreview.react.js b/src/components/ContainerHomePreview.react.js index 116b6d5b89..69bba09568 100644 --- a/src/components/ContainerHomePreview.react.js +++ b/src/components/ContainerHomePreview.react.js @@ -70,6 +70,7 @@ var ContainerHomePreview = React.createClass({ ); }); + preview = (

IP & Ports

diff --git a/src/components/ContainerListNewItem.react.js b/src/components/ContainerListNewItem.react.js index ab64e5792e..237e620c4a 100644 --- a/src/components/ContainerListNewItem.react.js +++ b/src/components/ContainerListNewItem.react.js @@ -1,13 +1,10 @@ var $ = require('jquery'); -var React = require('react/addons'); +var React = require('react'); var Router = require('react-router'); -var ContainerStore = require('../stores/ContainerStore'); var metrics = require('../utils/MetricsUtil'); var ContainerListNewItem = React.createClass({ - contextTypes: { - router: React.PropTypes.func - }, + mixins: [Router.Navigation, Router.State], handleItemMouseEnter: function () { var $action = $(this.getDOMNode()).find('.action'); $action.show(); @@ -16,22 +13,20 @@ var ContainerListNewItem = React.createClass({ var $action = $(this.getDOMNode()).find('.action'); $action.hide(); }, - handleDelete: function () { - var self = this; + handleDelete: function (event) { metrics.track('Deleted Container', { from: 'list', type: 'new' }); - var containers = ContainerStore.sorted(); - $(self.getDOMNode()).fadeOut(300, () => { - if (containers.length > 0) { - var name = containers[0].Name; - this.context.router.transitionTo('containerHome', {name: name}); - } - }); + + if (this.props.containers.length > 0 && this.getRoutes()[this.getRoutes().length - 2].name === 'new') { + var name = this.props.containers[0].Name; + this.transitionTo('containerHome', {name}); + } + $(this.getDOMNode()).fadeOut(300); + event.preventDefault(); }, render: function () { - var self = this; var action; if (this.props.containers.length > 0) { action = ( @@ -42,7 +37,7 @@ var ContainerListNewItem = React.createClass({ } return ( -
  • +
  • diff --git a/src/components/ContainerSettingsGeneral.react.js b/src/components/ContainerSettingsGeneral.react.js index 11798c4b3c..f2a910e0ea 100644 --- a/src/components/ContainerSettingsGeneral.react.js +++ b/src/components/ContainerSettingsGeneral.react.js @@ -1,5 +1,4 @@ var _ = require('underscore'); -var $ = require('jquery'); var React = require('react/addons'); var remote = require('remote'); var metrics = require('../utils/MetricsUtil'); @@ -8,22 +7,31 @@ var ContainerUtil = require('../utils/ContainerUtil'); var containerActions = require('../actions/ContainerActions'); var ContainerSettingsGeneral = React.createClass({ + mixins: [React.addons.LinkedStateMixin], + contextTypes: { router: React.PropTypes.func }, getInitialState: function () { + let env = ContainerUtil.env(this.props.container) || []; + env.push(['', '']); + console.log(env); return { slugName: null, nameError: null, - pendingEnv: ContainerUtil.env(this.props.container) || {} + env: env }; }, - willReceiveProps: function () { - this.setState({ - pendingEnv: ContainerUtil.env(this.props.container) || {} - }); + shouldComponentUpdate: function (nextProps, nextState) { + if (nextState.slugName !== this.state.slugName || nextState.nameError !== this.state.nameError) { + return true; + } + if (nextState.env.length === this.state.env.length) { + return false; + } + return true; }, handleNameChange: function (e) { @@ -78,39 +86,54 @@ var ContainerSettingsGeneral = React.createClass({ metrics.track('Changed Container Name'); }, - handleSaveEnvVar: function () { - var $rows = $('.env-vars .keyval-row'); - var envVarList = []; - $rows.each(function () { - var key = $(this).find('.key').val(); - var val = $(this).find('.val').val(); - if (!key.length || !val.length) { - return; - } - envVarList.push(key + '=' + val); - }); + handleSaveEnvVars: function () { metrics.track('Saved Environment Variables'); - containerActions.update(this.props.container.Name, {Env: envVarList}); + let list = []; + _.each(this.state.env, kvp => { + let [key, value] = kvp; + if ((key && key.length) || (value && value.length)) { + list.push(key + '=' + value); + } + }); + containerActions.update(this.props.container.Name, {Env: list}); }, - handleAddPendingEnvVar: function () { - var newKey = $('#new-env-key').val(); - var newVal = $('#new-env-val').val(); - var newEnv = {}; - newEnv[newKey] = newVal; + handleChangeEnvKey: function (index, event) { + let env = _.map(this.state.env, _.clone); + env[index][0] = event.target.value; this.setState({ - pendingEnv: _.extend(this.state.pendingEnv, newEnv) + env: env + }); + }, + + handleChangeEnvVal: function (index, event) { + let env = _.map(this.state.env, _.clone); + env[index][1] = event.target.value; + this.setState({ + env: env + }); + }, + + handleAddEnvVar: function () { + let env = _.map(this.state.env, _.clone); + env.push(['', '']); + this.setState({ + env: env }); - $('#new-env-key').val(''); - $('#new-env-val').val(''); metrics.track('Added Pending Environment Variable'); }, - handleRemovePendingEnvVar: function (key) { - var newEnv = _.omit(this.state.env, key); + handleRemoveEnvVar: function (index) { + let env = _.map(this.state.env, _.clone); + env.splice(index, 1); + if (env.length === 0) { + env.push(['', '']); + } + this.setState({ - env: newEnv + env: env }); + metrics.track('Removed Environment Variable'); }, @@ -131,8 +154,9 @@ var ContainerSettingsGeneral = React.createClass({ render: function () { if (!this.props.container) { - return (
    ); + return false; } + var willBeRenamedAs; var btnSaveName = ( Save @@ -149,7 +173,8 @@ var ContainerSettingsGeneral = React.createClass({

    {this.state.nameError}

    ); } - var rename = ( + + let rename = (

    Container Name

    @@ -159,15 +184,25 @@ var ContainerSettingsGeneral = React.createClass({ {btnSaveName}
    ); - var pendingEnvVars = _.map(this.state.pendingEnv, (val, key) => { + + let vars = _.map(this.state.env, (kvp, index) => { + let [key, val] = kvp; + let icon; + if (index === this.state.env.length - 1) { + icon = ; + } else { + icon = ; + } + return ( -
    - - - +
    { if (!filenames) { return; } @@ -21,11 +21,8 @@ var ContainerSettingsVolumes = React.createClass({ var binds = _.pairs(volumes).map(function (pair) { return pair[1] + ':' + pair[0]; }); - ContainerStore.updateContainer(self.props.container.Name, { - Binds: binds - }, function (err) { - if (err) { console.log(err); } - }); + + containerActions.update(this.props.container.Name, {Binds: binds}); } }); }, @@ -38,11 +35,7 @@ var ContainerSettingsVolumes = React.createClass({ var binds = _.pairs(volumes).map(function (pair) { return pair[1] + ':' + pair[0]; }); - ContainerStore.updateContainer(this.props.container.Name, { - Binds: binds - }, function (err) { - if (err) { console.log(err); } - }); + containerActions.update(this.props.container.Name, {Binds: binds}); }, handleOpenVolumeClick: function (path) { metrics.track('Opened Volume Directory', { diff --git a/src/components/Containers.react.js b/src/components/Containers.react.js index 9b6c525b80..a5829d82ce 100644 --- a/src/components/Containers.react.js +++ b/src/components/Containers.react.js @@ -1,6 +1,6 @@ var $ = require('jquery'); var _ = require('underscore'); -var React = require('react/addons'); +var React = require('react'); var Router = require('react-router'); var containerStore = require('../stores/ContainerStore'); var ContainerList = require('./ContainerList.react'); @@ -62,7 +62,9 @@ var Containers = React.createClass({ }); let name = this.context.router.getCurrentParams().name; - if (name && !containers[name]) { + if (containerStore.getState().pending) { + this.context.router.transitionTo('pull'); + } else if (name && !containers[name]) { if (sorted.length) { this.context.router.transitionTo('containerHome', {name: sorted[0].Name}); } else { @@ -72,7 +74,8 @@ var Containers = React.createClass({ this.setState({ containers: containers, - sorted: sorted + sorted: sorted, + pending: containerStore.getState().pending }); }, diff --git a/src/components/ImageCard.react.js b/src/components/ImageCard.react.js index a139a2f47d..67a0a0a229 100644 --- a/src/components/ImageCard.react.js +++ b/src/components/ImageCard.react.js @@ -1,14 +1,16 @@ var $ = require('jquery'); var React = require('react/addons'); +var Router = require('react-router'); var RetinaImage = require('react-retina-image'); var metrics = require('../utils/MetricsUtil'); var OverlayTrigger = require('react-bootstrap').OverlayTrigger; var Tooltip = require('react-bootstrap').Tooltip; var util = require('../utils/Util'); -var dockerUtil = require('../utils/DockerUtil'); +var containerActions = require('../actions/ContainerActions'); var containerStore = require('../stores/ContainerStore'); var ImageCard = React.createClass({ + mixins: [Router.Navigation], getInitialState: function () { return { tags: [], @@ -28,7 +30,8 @@ var ImageCard = React.createClass({ from: 'search' }); let name = containerStore.generateName(repository); - dockerUtil.run(name, repository, this.state.chosenTag); + containerActions.run(name, repository, this.state.chosenTag); + this.transitionTo('containerHome', {name}); }, handleTagOverlayClick: function (name) { var $tagOverlay = $(this.getDOMNode()).find('.tag-overlay'); diff --git a/src/components/NewContainerPull.react.js b/src/components/NewContainerPull.react.js index 3327b399f5..4a9150a962 100644 --- a/src/components/NewContainerPull.react.js +++ b/src/components/NewContainerPull.react.js @@ -1,30 +1,32 @@ var React = require('react/addons'); var Router = require('react-router'); var shell = require('shell'); -var ContainerStore = require('../stores/ContainerStore'); +var containerActions = require('../actions/ContainerActions'); +var containerStore = require('../stores/ContainerStore'); var metrics = require('../utils/MetricsUtil'); module.exports = React.createClass({ mixins: [Router.Navigation], handleOpenClick: function () { - var repo = this.props.pending.repository; + var repo = this.props.pending.repo; if (repo.indexOf('/') === -1) { - shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repository}`); + shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repo}`); } else { - shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repository}`); + shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repo}`); } }, handleCancelClick: function () { metrics.track('Canceled Click-To-Pull'); - ContainerStore.clearPending(); + containerActions.clearPending(); this.context.router.transitionTo('new'); }, handleConfirmClick: function () { metrics.track('Created Container', { from: 'click-to-pull' }); - ContainerStore.clearPending(); - ContainerStore.create(this.props.pending.repository, this.props.pending.tag, function () {}); + containerActions.clearPending(); + let name = containerStore.generateName(this.props.pending.repo); + containerActions.run(name, this.props.pending.repo, this.props.pending.tag); }, render: function () { if (!this.props.pending) { @@ -34,7 +36,7 @@ module.exports = React.createClass({
    -

    You're about to download and run {this.props.pending.repository}:{this.props.pending.tag}.

    +

    You're about to download and run {this.props.pending.repo}:{this.props.pending.tag}.

    Please confirm to create the container.

    Cancel Confirm diff --git a/src/stores/ContainerStore.js b/src/stores/ContainerStore.js index 8bdb4f632f..ddb8a9d676 100644 --- a/src/stores/ContainerStore.js +++ b/src/stores/ContainerStore.js @@ -11,6 +11,9 @@ class ContainerStore { // Blacklist of containers to avoid updating this.muted = {}; + + // Pending container to create + this.pending = null; } start ({name}) { @@ -106,7 +109,7 @@ class ContainerStore { this.setState({containers}); } - error ({ name, error }) { + error ({name, error}) { let containers = this.containers; if (containers[name]) { containers[name].Error = error; @@ -114,6 +117,15 @@ class ContainerStore { this.setState({containers}); } + pending ({repo, tag}) { + let pending = {repo, tag}; + this.setState({pending}); + } + + clearPending () { + this.setState({pending: null}); + } + static generateName (repo) { let base = _.last(repo.split('/')); let count = 1; diff --git a/src/utils/ContainerUtil.js b/src/utils/ContainerUtil.js index 2ddfdafcb4..4d6016ce6e 100644 --- a/src/utils/ContainerUtil.js +++ b/src/utils/ContainerUtil.js @@ -4,13 +4,13 @@ var docker = require('../utils/DockerUtil'); var ContainerUtil = { env: function (container) { if (!container || !container.Config || !container.Config.Env) { - return {}; + return []; } - return _.object(container.Config.Env.map(function (env) { + return _.map(container.Config.Env, env => { var i = env.indexOf('='); var splits = [env.slice(0, i), env.slice(i + 1)]; return splits; - })); + }); }, // TODO: inject host here instead of requiring Docker diff --git a/src/utils/URLUtil.js b/src/utils/URLUtil.js index 9f195ffdb4..2b4e8a8933 100644 --- a/src/utils/URLUtil.js +++ b/src/utils/URLUtil.js @@ -1,6 +1,6 @@ var util = require('./Util'); var parseUri = require('parseUri'); -var containerStore = require('../stores/ContainerStore'); +var containerServerActions = require('../actions/ContainerServerActions'); module.exports = { TYPE_WHITELIST: ['repository'], @@ -52,7 +52,7 @@ module.exports = { } if (type === 'repository' && method === 'run') { - containerStore.setPending(repo, 'latest'); + containerServerActions.pending({repo, tag: 'latest'}); return true; } return false;