From e92678508b86251ece3f794c5ff91239476517e2 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 11 Mar 2015 18:43:19 -0400 Subject: [PATCH 01/10] WIP button --- src/ContainerStore.js | 14 +++++++++++++- src/Main.js | 19 +++++++++++++------ src/browser.js | 21 +++++++++++++++++++++ util/Info.plist | 11 +++++++++++ 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 121033316f..64db81119b 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -16,6 +16,7 @@ var _progress = {}; var _muted = {}; var _blocked = {}; var _error = null; +var _pendingCreates = []; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', @@ -238,7 +239,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } }, init: function (callback) { - // TODO: Load cached data from db on loading this.fetchAllContainers(err => { if (err) { _error = err; @@ -249,6 +249,9 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } else { callback(); } + _pendingCreates.forEach(e => { + this.create(e.repository, e.tag, () => {}); + }); var placeholderData = JSON.parse(localStorage.getItem('store.placeholders')); if (placeholderData) { _placeholders = _.omit(placeholderData, _.keys(_containers)); @@ -305,6 +308,15 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, create: function (repository, tag, callback) { + if (!docker.host()) { + console.log('buffering ' + repository); + _pendingCreates.push({ + repository: repository, + tag: tag + }); + return; + } + tag = tag || 'latest'; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); diff --git a/src/Main.js b/src/Main.js index 9f582f3588..ffc14d3f71 100644 --- a/src/Main.js +++ b/src/Main.js @@ -27,6 +27,19 @@ setInterval(function () { router.run(Handler => React.render(, document.body)); +ipc.on('application:quitting', opts => { + if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { + machine.stop(); + } +}); + +ipc.on('application:open-url', opts => { + console.log('Creating container'); + var url = opts.url.substr(19); + console.log(url); + ContainerStore.create(url, 'latest', () => {}); +}); + SetupStore.setup().then(() => { Menu.setApplicationMenu(Menu.buildFromTemplate(template())); ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => { @@ -43,9 +56,3 @@ SetupStore.setup().then(() => { console.log(err); bugsnag.notify(err); }); - -ipc.on('application:quitting', opts => { - if (!opts.updating && localStorage.getItem('settings.closeVMOnQuit') === 'true') { - machine.stop(); - } -}); diff --git a/src/browser.js b/src/browser.js index 8722673dcb..9ba05e9576 100644 --- a/src/browser.js +++ b/src/browser.js @@ -10,6 +10,8 @@ process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); process.chdir(path.join(__dirname, '..')); process.env.PATH = '/usr/local/bin:' + process.env.PATH; +console.log(process.argv); + var size = {}, settingsjson = {}; try { size = JSON.parse(fs.readFileSync(path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], 'Library', 'Application\ Support', 'Kitematic', 'size'))); @@ -18,6 +20,13 @@ try { settingsjson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'settings.json'), 'utf8')); } catch (err) {} + +var openURL = null; +app.on('open-url', function (event, url) { + event.preventDefault(); + openURL = url; +}); + app.on('ready', function () { var mainWindow = new BrowserWindow({ width: size.width || 1000, @@ -65,6 +74,18 @@ app.on('ready', function () { mainWindow.show(); mainWindow.focus(); + if (openURL) { + mainWindow.webContents.send('application:open-url', { + url: openURL + }); + } + app.on('open-url', function (event, url) { + event.preventDefault(); + mainWindow.webContents.send('application:open-url', { + url: url + }); + }); + if (process.env.NODE_ENV !== 'development') { autoUpdater.setFeedUrl('https://updates.kitematic.com/releases/latest?version=' + app.getVersion() + '&beta=' + !!settingsjson.beta); } diff --git a/util/Info.plist b/util/Info.plist index 396be69183..6a030f001b 100644 --- a/util/Info.plist +++ b/util/Info.plist @@ -26,5 +26,16 @@ AtomApplication NSSupportsAutomaticGraphicsSwitching + CFBundleURLTypes + + + CFBundleURLSchemes + + kitematic + + CFBundleURLName + Kitematic Session Protocol + + From 9e2c11f6d0d6322ad652627d704419c3d42b70b7 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 18 Mar 2015 11:51:59 -0700 Subject: [PATCH 02/10] Changing protocol to Docker --- src/Main.js | 20 ++++++++++++++++---- util/Info.plist | 4 ++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Main.js b/src/Main.js index ffc14d3f71..79f96e9ed1 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,10 +34,22 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { - console.log('Creating container'); - var url = opts.url.substr(19); - console.log(url); - ContainerStore.create(url, 'latest', () => {}); + console.log('Creating container from protocol'); + var parser = document.createElement('a'); + parser.href = opts.url; + + if (parser.protocol !== 'docker:') { + return; + } + + var pathname = parser.pathname.replace('//', ''); + var slash = pathname.indexOf('/'); + var base = pathname.slice(0, slash); + + if (base === 'runRepo') { + var repo = pathname.substring(slash + 1); + ContainerStore.create(repo, 'latest', () => {}); + } }); SetupStore.setup().then(() => { diff --git a/util/Info.plist b/util/Info.plist index 6a030f001b..5efd29e633 100644 --- a/util/Info.plist +++ b/util/Info.plist @@ -31,10 +31,10 @@ CFBundleURLSchemes - kitematic + docker CFBundleURLName - Kitematic Session Protocol + Docker App Protocol From 89d3728bda1b63c951101b6c4c7e82429816f45c Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 6 Apr 2015 14:19:47 -0400 Subject: [PATCH 03/10] Removing console side --- src/browser.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/browser.js b/src/browser.js index 9ba05e9576..ee7810450a 100644 --- a/src/browser.js +++ b/src/browser.js @@ -10,8 +10,6 @@ process.env.RESOURCES_PATH = path.join(__dirname, '/../resources'); process.chdir(path.join(__dirname, '..')); process.env.PATH = '/usr/local/bin:' + process.env.PATH; -console.log(process.argv); - var size = {}, settingsjson = {}; try { size = JSON.parse(fs.readFileSync(path.join(process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'], 'Library', 'Application\ Support', 'Kitematic', 'size'))); From d7dea0bcfadfcef6c58be71e6fc68731b76020a1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Mon, 6 Apr 2015 21:45:45 -0400 Subject: [PATCH 04/10] wip button Signed-off-by: Jeffrey Morgan --- src/ContainerStore.js | 32 +++++++++++++++++++------------- src/Containers.react.js | 20 +++++++++++++++----- src/Main.js | 3 ++- src/NewContainer.react.js | 3 ++- src/PullContainer.react.js | 16 ++++++++++++++++ src/Routes.js | 2 ++ 6 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 src/PullContainer.react.js diff --git a/src/ContainerStore.js b/src/ContainerStore.js index f3c0f5613c..d4a7615f31 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -14,7 +14,7 @@ var _progress = {}; var _muted = {}; var _blocked = {}; var _error = null; -var _pendingCreates = []; +var _pending = null; var ContainerStore = assign(Object.create(EventEmitter.prototype), { CLIENT_CONTAINER_EVENT: 'client_container_event', @@ -237,9 +237,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { } else { callback(); } - _pendingCreates.forEach(e => { - this.create(e.repository, e.tag, () => {}); - }); var placeholderData = JSON.parse(localStorage.getItem('store.placeholders')); if (placeholderData) { _placeholders = _.omit(placeholderData, _.keys(_containers)); @@ -296,19 +293,14 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }); }, create: function (repository, tag, callback) { - if (!docker.host()) { - console.log('buffering ' + repository); - _pendingCreates.push({ - repository: repository, - tag: tag - }); - return; - } - tag = tag || 'latest'; var imageName = repository + ':' + tag; var containerName = this._generateName(repository); + if (_pending && _pending.repository === repository && _pending.tag === tag) { + _pending = null; + } + _placeholders[containerName] = { Name: containerName, Image: imageName, @@ -483,6 +475,20 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { }, downloading: function () { return !!_.keys(_placeholders).length; + }, + pending: function () { + return _pending; + }, + setPending: function (repository, tag) { + _pending = { + repository: repository, + tag: tag + }; + this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); + }, + clearPending: function () { + _pending = null; + this.emit(this.CLIENT_CONTAINER_EVENT, null, 'pending'); } }); diff --git a/src/Containers.react.js b/src/Containers.react.js index af151dabca..62f316c1b2 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -33,7 +33,11 @@ var Containers = React.createClass({ ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); - if (this.state.sorted.length) { + console.log(this.state); + if (this.state.pending) { + console.log('pending'); + this.transitionTo('pull'); + } else if (this.state.sorted.length) { this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); } @@ -64,7 +68,8 @@ var Containers = React.createClass({ this.setState({ containers: ContainerStore.containers(), sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading() + downloading: ContainerStore.downloading(), + pending: ContainerStore.pending() }); if (status === 'destroy') { this.onDestroy(); @@ -74,9 +79,14 @@ var Containers = React.createClass({ this.setState({ containers: ContainerStore.containers(), sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading() + downloading: ContainerStore.downloading(), + pending: ContainerStore.pending() }); - if (status === 'create') { + console.log('update from client'); + + if (this.state.pending) { + this.transitionTo('pull'); + } else if (status === 'create') { this.transitionTo('containerHome', {name: name}); } else if (status === 'destroy') { this.onDestroy(); @@ -197,7 +207,7 @@ var Containers = React.createClass({
- + ); diff --git a/src/Main.js b/src/Main.js index 79f96e9ed1..72588cbac0 100644 --- a/src/Main.js +++ b/src/Main.js @@ -48,9 +48,10 @@ ipc.on('application:open-url', opts => { if (base === 'runRepo') { var repo = pathname.substring(slash + 1); - ContainerStore.create(repo, 'latest', () => {}); + ContainerStore.setPending(repo, 'latest'); } }); +ContainerStore.setPending('kitematic/ghost', 'latest'); SetupStore.setup().then(() => { Menu.setApplicationMenu(Menu.buildFromTemplate(template())); diff --git a/src/NewContainer.react.js b/src/NewContainer.react.js index 05e772cedd..eb8059a31a 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainer.react.js @@ -4,8 +4,8 @@ var React = require('react/addons'); var RetinaImage = require('react-retina-image'); var Radial = require('./Radial.react'); var ImageCard = require('./ImageCard.react'); -var Promise = require('bluebird'); var metrics = require('./Metrics'); +var Promise = require('bluebird'); var _recommended = []; var _searchPromise = null; @@ -142,6 +142,7 @@ var NewContainer = React.createClass({ 'icon-magnifier': true, 'search-icon': true }); + return (
diff --git a/src/PullContainer.react.js b/src/PullContainer.react.js new file mode 100644 index 0000000000..0fa81925ee --- /dev/null +++ b/src/PullContainer.react.js @@ -0,0 +1,16 @@ +var React = require('react/addons'); + +var PullContainer = React.createClass({ + componentDidMount: function () { + }, + render: function () { + console.log(this.props.pending); + return ( +
+ test +
+ ); + } +}); + +module.exports = PullContainer; diff --git a/src/Routes.js b/src/Routes.js index eab468fa1c..05dd638a0e 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -10,6 +10,7 @@ var ContainerSettingsPorts = require('./ContainerSettingsPorts.react'); var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react'); var Preferences = require('./Preferences.react'); var NewContainer = require('./NewContainer.react'); +var PullContainer = require('./PullContainer.react'); var Router = require('react-router'); var Route = Router.Route; @@ -38,6 +39,7 @@ var routes = ( + From a5b003b381550cf05759ac587ba4fc6b9b68c054 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 7 Apr 2015 16:06:08 -0400 Subject: [PATCH 05/10] Adding pull container feature --- .jshintrc | 2 +- src/ContainerList.react.js | 8 +-- src/ContainerStore.js | 4 -- src/Containers.react.js | 60 +++++-------------- src/Main.js | 10 ++-- src/NewContainerPull.react.js | 42 +++++++++++++ ...r.react.js => NewContainerSearch.react.js} | 9 +-- src/Routes.js | 26 ++++---- styles/new-container.less | 29 +++++++++ styles/theme.less | 1 - 10 files changed, 111 insertions(+), 80 deletions(-) create mode 100644 src/NewContainerPull.react.js rename src/{NewContainer.react.js => NewContainerSearch.react.js} (95%) diff --git a/.jshintrc b/.jshintrc index daeaf45072..9240a3ae9f 100644 --- a/.jshintrc +++ b/.jshintrc @@ -27,5 +27,5 @@ "jest": true, "pit": true }, - "predef": [ "Promise" ] + "predef": [ "-Promise" ] } diff --git a/src/ContainerList.react.js b/src/ContainerList.react.js index 1d6d25a034..dd2ae9b139 100644 --- a/src/ContainerList.react.js +++ b/src/ContainerList.react.js @@ -13,15 +13,9 @@ var ContainerList = React.createClass({ ); }); - var newItem; - if (!this.props.downloading) { - newItem = ; - } else { - newItem = ''; - } return (
    - {newItem} + {containers}
); diff --git a/src/ContainerStore.js b/src/ContainerStore.js index d4a7615f31..70de5b65a5 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -297,10 +297,6 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var imageName = repository + ':' + tag; var containerName = this._generateName(repository); - if (_pending && _pending.repository === repository && _pending.tag === tag) { - _pending = null; - } - _placeholders[containerName] = { Name: containerName, Image: imageName, diff --git a/src/Containers.react.js b/src/Containers.react.js index 62f316c1b2..ab613bb61a 100644 --- a/src/Containers.react.js +++ b/src/Containers.react.js @@ -10,8 +10,6 @@ var metrics = require('./Metrics'); var autoUpdater = remote.require('auto-updater'); var RetinaImage = require('react-retina-image'); var machine = require('./DockerMachine'); -var OverlayTrigger = require('react-bootstrap').OverlayTrigger; -var Tooltip = require('react-bootstrap').Tooltip; var util = require('./Util'); var Containers = React.createClass({ @@ -33,14 +31,6 @@ var Containers = React.createClass({ ContainerStore.on(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.on(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); - console.log(this.state); - if (this.state.pending) { - console.log('pending'); - this.transitionTo('pull'); - } else if (this.state.sorted.length) { - this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); - } - ipc.on('application:update-available', () => { this.setState({ updateAvailable: true @@ -52,44 +42,33 @@ var Containers = React.createClass({ ContainerStore.removeListener(ContainerStore.SERVER_CONTAINER_EVENT, this.update); ContainerStore.removeListener(ContainerStore.CLIENT_CONTAINER_EVENT, this.updateFromClient); }, - onDestroy: function () { - if (this.state.sorted.length) { - this.transitionTo('containerHome', {name: this.state.sorted[0].Name}); - } else { - this.transitionTo('containers'); - } - }, updateError: function (err) { this.setState({ error: err }); }, update: function (name, status) { + var sorted = ContainerStore.sorted(); this.setState({ containers: ContainerStore.containers(), - sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading(), - pending: ContainerStore.pending() + sorted: sorted, + pending: ContainerStore.pending(), + downloading: ContainerStore.downloading() }); if (status === 'destroy') { - this.onDestroy(); + if (sorted.length) { + this.transitionTo('containerHome', {name: sorted[0].Name}); + } else { + this.transitionTo('containers'); + } } }, updateFromClient: function (name, status) { - this.setState({ - containers: ContainerStore.containers(), - sorted: ContainerStore.sorted(), - downloading: ContainerStore.downloading(), - pending: ContainerStore.pending() - }); - console.log('update from client'); - - if (this.state.pending) { - this.transitionTo('pull'); - } else if (status === 'create') { + this.update(name, status); + if (status === 'create') { this.transitionTo('containerHome', {name: name}); - } else if (status === 'destroy') { - this.onDestroy(); + } else if (status === 'pending' && ContainerStore.pending()) { + this.transitionTo('pull'); } }, handleScroll: function (e) { @@ -172,17 +151,6 @@ var Containers = React.createClass({ ); } - var button; - if (this.state.downloading) { - button = ( - Only one Docker image can be downloaded at a time.}> - - - ); - } else { - button = ; - } - var container = this.getParams().name ? this.state.containers[this.getParams().name] : {}; return (
@@ -192,7 +160,7 @@ var Containers = React.createClass({

Containers

- {button} +
diff --git a/src/Main.js b/src/Main.js index 72588cbac0..729025ce88 100644 --- a/src/Main.js +++ b/src/Main.js @@ -51,16 +51,18 @@ ipc.on('application:open-url', opts => { ContainerStore.setPending(repo, 'latest'); } }); -ContainerStore.setPending('kitematic/ghost', 'latest'); SetupStore.setup().then(() => { + if (ContainerStore.pending()) { + router.transitionTo('pull'); + } else { + router.transitionTo('new'); + } Menu.setApplicationMenu(Menu.buildFromTemplate(template())); ContainerStore.on(ContainerStore.SERVER_ERROR_EVENT, (err) => { bugsnag.notify(err); }); - ContainerStore.init(function () { - router.transitionTo('containers'); - }); + ContainerStore.init(function () {}); }).catch(err => { metrics.track('Setup Failed', { step: 'catch', diff --git a/src/NewContainerPull.react.js b/src/NewContainerPull.react.js new file mode 100644 index 0000000000..3efd71aab5 --- /dev/null +++ b/src/NewContainerPull.react.js @@ -0,0 +1,42 @@ +var React = require('react/addons'); +var Router = require('react-router'); +var shell = require('shell'); +var ContainerStore = require('./ContainerStore'); + +module.exports = React.createClass({ + mixins: [Router.Navigation], + handleOpenClick: function () { + var repo = this.props.pending.repository; + if (repo.indexOf('/') === -1) { + shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repository}`); + } else { + shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repository}`); + } + }, + handleCancelClick: function () { + ContainerStore.clearPending(); + this.transitionTo('new'); + }, + handleConfirmClick: function () { + ContainerStore.clearPending(); + ContainerStore.create(this.props.pending.repository, this.props.pending.tag, function () {}); + }, + render: function () { + if (!this.props.pending) { + return false; + } + return ( +
+
+
+

You're about to download {this.props.pending.repository}.

+

Please confirm to create the container.

+ +
+
+
+ ); + } +}); diff --git a/src/NewContainer.react.js b/src/NewContainerSearch.react.js similarity index 95% rename from src/NewContainer.react.js rename to src/NewContainerSearch.react.js index eb8059a31a..3ac67f2882 100644 --- a/src/NewContainer.react.js +++ b/src/NewContainerSearch.react.js @@ -4,13 +4,13 @@ var React = require('react/addons'); var RetinaImage = require('react-retina-image'); var Radial = require('./Radial.react'); var ImageCard = require('./ImageCard.react'); -var metrics = require('./Metrics'); var Promise = require('bluebird'); +var metrics = require('./Metrics'); var _recommended = []; var _searchPromise = null; -var NewContainer = React.createClass({ +module.exports = React.createClass({ getInitialState: function () { return { query: '', @@ -47,7 +47,7 @@ var NewContainer = React.createClass({ loading: true }); - _searchPromise = Promise.delay(200).then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).cancellable().then(data => { + _searchPromise = Promise.delay(200).cancellable().then(() => Promise.resolve($.get('https://registry.hub.docker.com/v1/search?q=' + query))).then(data => { metrics.track('Searched for Images'); this.setState({ results: data.results, @@ -142,7 +142,6 @@ var NewContainer = React.createClass({ 'icon-magnifier': true, 'search-icon': true }); - return (
@@ -167,5 +166,3 @@ var NewContainer = React.createClass({ ); } }); - -module.exports = NewContainer; diff --git a/src/Routes.js b/src/Routes.js index 05dd638a0e..cfdade8b51 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -9,13 +9,14 @@ var ContainerSettingsGeneral = require('./ContainerSettingsGeneral.react'); var ContainerSettingsPorts = require('./ContainerSettingsPorts.react'); var ContainerSettingsVolumes = require('./ContainerSettingsVolumes.react'); var Preferences = require('./Preferences.react'); -var NewContainer = require('./NewContainer.react'); -var PullContainer = require('./PullContainer.react'); +var NewContainerSearch = require('./NewContainerSearch.react'); +var NewContainerPull = require('./NewContainerPull.react'); var Router = require('react-router'); var Route = Router.Route; var DefaultRoute = Router.DefaultRoute; var RouteHandler = Router.RouteHandler; +var Redirect = Router.Redirect; var App = React.createClass({ render: function () { @@ -28,18 +29,21 @@ var App = React.createClass({ var routes = ( - - - - - - - + + + + + + + + + + + - - + diff --git a/styles/new-container.less b/styles/new-container.less index a00f9c3eb3..86b60d8903 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -1,3 +1,32 @@ +.new-container-pull { + display: flex; + flex: 1 auto; + align-items: center; + justify-content: center; + .content { + text-align: center; + + .buttons { + margin-top: 30px; + .btn { + margin-left: 10px; + margin-right: 10px; + padding: 8px 18px; + font-size: 14px; + background: white; + font-weight: 300; + } + } + } + h1 { + font-size: 20px; + color: @gray-normal; + font-weight: 400; + text-align: center; + margin-top: 10px; + } +} + .new-container { display: flex; flex: 1 auto; diff --git a/styles/theme.less b/styles/theme.less index 07e8cedd22..d20daccc76 100644 --- a/styles/theme.less +++ b/styles/theme.less @@ -134,7 +134,6 @@ input[type="text"] { font-weight: 400; text-shadow: none; padding: 5px 14px 5px 14px; - height: 30px; cursor: default; &.small { From b06999e36aa72fcf925b004d74ca085963b55dd1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Sat, 11 Apr 2015 17:59:03 -0400 Subject: [PATCH 06/10] New click-to-pull protocol Signed-off-by: Jeffrey Morgan --- src/ContainerStore.js | 4 ++++ src/Main.js | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/ContainerStore.js b/src/ContainerStore.js index 70de5b65a5..262ddc6a57 100644 --- a/src/ContainerStore.js +++ b/src/ContainerStore.js @@ -202,6 +202,10 @@ var ContainerStore = assign(Object.create(EventEmitter.prototype), { var data = JSON.parse(json); console.log(data); + if (data.status === 'pull' || data.status === 'untag' || data.status === 'delete') { + return; + } + // If the event is delete, remove the container if (data.status === 'destroy') { var container = _.findWhere(_.values(_containers), {Id: data.id}); diff --git a/src/Main.js b/src/Main.js index 729025ce88..9726bc67d0 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,7 +34,6 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { - console.log('Creating container from protocol'); var parser = document.createElement('a'); parser.href = opts.url; @@ -43,12 +42,12 @@ ipc.on('application:open-url', opts => { } var pathname = parser.pathname.replace('//', ''); - var slash = pathname.indexOf('/'); - var base = pathname.slice(0, slash); - - if (base === 'runRepo') { - var repo = pathname.substring(slash + 1); - ContainerStore.setPending(repo, 'latest'); + var tokens = pathname.split('/'); + var type = tokens[0]; + var method = tokens[1]; + var repo = tokens.slice(2).join('/'); + if (type === 'repository' && method === 'run') { + ContainerStore.setPending(repo, parser.tag || 'latest'); } }); From 6679f9a85113c4e1246d5fe590572f0ae67c76d1 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Tue, 14 Apr 2015 13:26:27 -0400 Subject: [PATCH 07/10] Check URL parameters via regexp Signed-off-by: Jeffrey Morgan --- src/ContainerHomeFolders.react.js | 34 +++++++++++++++++++------------ src/Main.js | 15 ++++++++++---- src/NewContainerPull.react.js | 2 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/ContainerHomeFolders.react.js b/src/ContainerHomeFolders.react.js index 1ace6d4e90..2e3e796cc3 100644 --- a/src/ContainerHomeFolders.react.js +++ b/src/ContainerHomeFolders.react.js @@ -7,6 +7,7 @@ var util = require('./Util'); var metrics = require('./Metrics'); var Router = require('react-router'); var ContainerStore = require('./ContainerStore'); +var dialog = require('remote').require('dialog'); var ContainerHomeFolder = React.createClass({ mixins: [Router.State, Router.Navigation], @@ -16,20 +17,27 @@ var ContainerHomeFolder = React.createClass({ }); if (hostVolume.indexOf(process.env.HOME) === -1) { - var volumes = _.clone(this.props.container.Volumes); - var newHostVolume = path.join(util.home(), 'Kitematic', this.props.container.Name, containerVolume); - volumes[containerVolume] = newHostVolume; - 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); - return; + dialog.showMessageBox({ + message: 'Enable all volumes to edit files via Finder? This may not work with all database containers.', + buttons: ['Enable Volumes', 'Cancel'] + }, (index) => { + if (index === 0) { + var volumes = _.clone(this.props.container.Volumes); + var newHostVolume = path.join(util.home(), 'Kitematic', this.props.container.Name, containerVolume); + volumes[containerVolume] = newHostVolume; + var binds = _.pairs(volumes).map(function (pair) { + return pair[1] + ':' + pair[0]; + }); + ContainerStore.updateContainer(this.props.container.Name, { + Binds: binds + }, (err) => { + if (err) { + console.log(err); + return; + } + shell.showItemInFolder(newHostVolume); + }); } - shell.showItemInFolder(newHostVolume); }); } else { shell.showItemInFolder(hostVolume); diff --git a/src/Main.js b/src/Main.js index 9726bc67d0..4400f3ee29 100644 --- a/src/Main.js +++ b/src/Main.js @@ -34,6 +34,8 @@ ipc.on('application:quitting', opts => { }); ipc.on('application:open-url', opts => { + var repoRegexp = /[a-z0-9]+(?:[._-][a-z0-9]+)*/; + var tagRegexp = /[\w][\w.-]{0,127}/; var parser = document.createElement('a'); parser.href = opts.url; @@ -44,10 +46,15 @@ ipc.on('application:open-url', opts => { var pathname = parser.pathname.replace('//', ''); var tokens = pathname.split('/'); var type = tokens[0]; - var method = tokens[1]; - var repo = tokens.slice(2).join('/'); - if (type === 'repository' && method === 'run') { - ContainerStore.setPending(repo, parser.tag || 'latest'); + var repo = tokens.slice(1).join('/'); + var tag = parser.tag || 'latest'; + + if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { + return; + } + + if (type === 'repository') { + ContainerStore.setPending(repo, tag); } }); diff --git a/src/NewContainerPull.react.js b/src/NewContainerPull.react.js index 3efd71aab5..bc915b4dda 100644 --- a/src/NewContainerPull.react.js +++ b/src/NewContainerPull.react.js @@ -29,7 +29,7 @@ module.exports = React.createClass({
-

You're about to download {this.props.pending.repository}.

+

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

Please confirm to create the container.

Cancel Confirm From e4491bd8999796305a4b0b31a2e8e72393d89041 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 13:15:44 -0400 Subject: [PATCH 08/10] Adding 'run' method back Signed-off-by: Jeffrey Morgan --- src/Main.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Main.js b/src/Main.js index 4400f3ee29..aa6ab5abdb 100644 --- a/src/Main.js +++ b/src/Main.js @@ -46,14 +46,15 @@ ipc.on('application:open-url', opts => { var pathname = parser.pathname.replace('//', ''); var tokens = pathname.split('/'); var type = tokens[0]; - var repo = tokens.slice(1).join('/'); + var method = tokens[1]; + var repo = tokens.slice(2).join('/'); var tag = parser.tag || 'latest'; if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { return; } - if (type === 'repository') { + if (type === 'repository' && method === 'run') { ContainerStore.setPending(repo, tag); } }); From ebc1143723dc26dc9dddbac3b6ef4429a584d610 Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 15:28:50 -0400 Subject: [PATCH 09/10] removing tag support for custom url --- src/Main.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Main.js b/src/Main.js index aa6ab5abdb..18d2819264 100644 --- a/src/Main.js +++ b/src/Main.js @@ -35,7 +35,6 @@ ipc.on('application:quitting', opts => { ipc.on('application:open-url', opts => { var repoRegexp = /[a-z0-9]+(?:[._-][a-z0-9]+)*/; - var tagRegexp = /[\w][\w.-]{0,127}/; var parser = document.createElement('a'); parser.href = opts.url; @@ -48,14 +47,13 @@ ipc.on('application:open-url', opts => { var type = tokens[0]; var method = tokens[1]; var repo = tokens.slice(2).join('/'); - var tag = parser.tag || 'latest'; - if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo) || !tagRegexp.test(tag)) { + if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo)) { return; } if (type === 'repository' && method === 'run') { - ContainerStore.setPending(repo, tag); + ContainerStore.setPending(repo, 'latest'); } }); From 4b1291c952f740c0c94c54cb325a486db70e9e1d Mon Sep 17 00:00:00 2001 From: Jeffrey Morgan Date: Wed, 15 Apr 2015 15:46:45 -0400 Subject: [PATCH 10/10] Add comment for supported repos --- src/Main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Main.js b/src/Main.js index 18d2819264..44235be0bc 100644 --- a/src/Main.js +++ b/src/Main.js @@ -48,6 +48,7 @@ ipc.on('application:open-url', opts => { var method = tokens[1]; var repo = tokens.slice(2).join('/'); + // Only accept official repos for now if (repo.indexOf('/') !== -1 || !repoRegexp.test(repo)) { return; }