diff --git a/Gruntfile.js b/Gruntfile.js index 7624bb252b..0b66b1426b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -315,7 +315,7 @@ module.exports = function (grunt) { grunt.registerTask('default', ['download-binary', 'newer:babel', 'less', 'newer:copy:dev', 'shell:electron', 'watchChokidar']); if (process.platform === 'win32') { - grunt.registerTask('release', ['clean:release', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'compress', 'create-windows-installer', 'rename:installer']); + grunt.registerTask('release', ['clean:release', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:windows', 'copy:windows', 'rcedit:exes', 'compress']); } else { grunt.registerTask('release', ['clean:release', 'download-binary', 'babel', 'less', 'copy:dev', 'electron:osx', 'copy:osx', 'shell:sign', 'shell:zip']); } diff --git a/__tests__/SetupStore-test.js b/__tests__/SetupStore-test.js index 184a2f3185..7436f140a8 100644 --- a/__tests__/SetupStore-test.js +++ b/__tests__/SetupStore-test.js @@ -55,7 +55,6 @@ describe('SetupStore', function () { machine.stop.mockReturnValue(Promise.resolve()); machine.start.mockReturnValue(Promise.resolve()); machine.upgrade.mockReturnValue(Promise.resolve()); - util.packagejson.mockReturnValue({'docker-version':'1.8.0'}); util.compareVersions.mockReturnValue(-1); machine.create.mockClear(); machine.upgrade.mockClear(); diff --git a/package.json b/package.json index 74dde77156..dc0104c73c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Kitematic", - "version": "0.8.5", + "version": "0.8.6", "author": "Kitematic", "description": "Simple Docker Container management for Mac OS X.", "homepage": "https://kitematic.com/", diff --git a/src/app.js b/src/app.js index 7c2c2bfe38..45f46a4d54 100644 --- a/src/app.js +++ b/src/app.js @@ -14,7 +14,6 @@ import request from 'request'; import docker from './utils/DockerUtil'; import hub from './utils/HubUtil'; import Router from 'react-router'; -import createHashHistory from 'history/lib/createHashHistory' import routes from './routes'; import routerContainer from './router'; import repositoryActions from './actions/RepositoryActions'; @@ -41,16 +40,19 @@ setInterval(function () { metrics.track('app heartbeat'); }, 14400000); -let history = createHashHistory() -React.render({routes}, document.body) +var router = Router.create({ + routes: routes +}); +router.run(Handler => React.render(, document.body)); +routerContainer.set(router); setupUtil.setup().then(() => { Menu.setApplicationMenu(Menu.buildFromTemplate(template())); docker.init(); if (!hub.prompted() && !hub.loggedin()) { - history.replaceState(null, '/account/login'); + router.transitionTo('login'); } else { - history.replaceState(null, '/containers/new'); + router.transitionTo('search'); } }).catch(err => { metrics.track('Setup Failed', { diff --git a/src/browser.js b/src/browser.js index 61d5f74273..9908b04041 100644 --- a/src/browser.js +++ b/src/browser.js @@ -74,6 +74,7 @@ app.on('ready', function () { mainWindow.webContents.send('application:quitting'); return true; }); + app.on('window-all-closed', function() { app.quit(); }); diff --git a/src/components/About.react.js b/src/components/About.react.js index 7ddd23ba36..1793da5810 100644 --- a/src/components/About.react.js +++ b/src/components/About.react.js @@ -39,7 +39,6 @@ var Preferences = React.createClass({

Docker Engine

-

{packages["docker-version"]}

diff --git a/src/components/ContainerHome.react.js b/src/components/ContainerHome.react.js index cd9aeab3a0..51867620d7 100644 --- a/src/components/ContainerHome.react.js +++ b/src/components/ContainerHome.react.js @@ -1,7 +1,6 @@ import _ from 'underscore'; import $ from 'jquery'; import React from 'react/addons'; -import Radial from './Radial.react'; import ContainerProgress from './ContainerProgress.react'; import ContainerHomePreview from './ContainerHomePreview.react'; import ContainerHomeLogs from './ContainerHomeLogs.react'; @@ -88,14 +87,14 @@ var ContainerHome = React.createClass({ body = (

Waiting For Another Download

- +
); } else { body = (

Connecting to Docker Hub

- +
); } diff --git a/src/components/Containers.react.js b/src/components/Containers.react.js index 492d76cba7..d7b611168f 100644 --- a/src/components/Containers.react.js +++ b/src/components/Containers.react.js @@ -10,6 +10,10 @@ import shell from 'shell'; import machine from '../utils/DockerMachineUtil'; var Containers = React.createClass({ + contextTypes: { + router: React.PropTypes.func + }, + getInitialState: function () { return { sidebarOffset: 0, @@ -48,9 +52,7 @@ var Containers = React.createClass({ let containers = containerStore.getState().containers; let sorted = this.sorted(containerStore.getState().containers); - console.log(this.props); - - let name = this.props.params.name; + let name = this.context.router.getCurrentParams().name; if (containerStore.getState().pending) { this.context.router.transitionTo('pull'); } else if (name && !containers[name]) { @@ -82,7 +84,7 @@ var Containers = React.createClass({ handleNewContainer: function () { $(this.getDOMNode()).find('.new-container-item').parent().fadeIn(); - this.context.router.transitionTo('new'); + this.context.router.transitionTo('search'); metrics.track('Pressed New Container'); }, @@ -149,7 +151,7 @@ var Containers = React.createClass({ sidebarHeaderClass += ' sep'; } - var container = this.props.params ? this.state.containers[ this.props.params] : {}; + var container = this.context.router.getCurrentParams().name ? this.state.containers[this.context.router.getCurrentParams().name] : {}; return (
@@ -158,7 +160,7 @@ var Containers = React.createClass({

Containers

- + New
diff --git a/src/components/Loading.react.js b/src/components/Loading.react.js new file mode 100644 index 0000000000..0f63f86349 --- /dev/null +++ b/src/components/Loading.react.js @@ -0,0 +1,15 @@ +import React from 'react/addons'; +import Header from './Header.react'; + +module.exports = React.createClass({ + render: function () { + return ( +
+
+
+
+
+
+ ); + } +}); diff --git a/src/components/NewContainerPull.react.js b/src/components/NewContainerPull.react.js deleted file mode 100644 index c8e5bdd74c..0000000000 --- a/src/components/NewContainerPull.react.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react/addons'; -import Router from 'react-router'; -import shell from 'shell'; -import containerActions from '../actions/ContainerActions'; -import containerStore from '../stores/ContainerStore'; -import metrics from '../utils/MetricsUtil'; - -module.exports = React.createClass({ - mixins: [Router.Navigation], - handleOpenClick: function () { - var repo = this.props.pending.repo; - if (repo.indexOf('/') === -1) { - shell.openExternal(`https://registry.hub.docker.com/_/${this.props.pending.repo}`); - } else { - shell.openExternal(`https://registry.hub.docker.com/u/${this.props.pending.repo}`); - } - }, - handleCancelClick: function () { - metrics.track('Canceled Click-To-Pull'); - containerActions.clearPending(); - this.transitionTo('new'); - }, - handleConfirmClick: function () { - metrics.track('Created Container', { - from: 'click-to-pull' - }); - containerActions.clearPending(); - let name = containerStore.generateName(this.props.pending.repo); - containerActions.run(name, this.props.pending.repo, this.props.pending.tag); - this.transitionTo('containerHome', {name}); - }, - render: function () { - if (!this.props.pending) { - return false; - } - return ( -
-
-
-

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

-

Please confirm to create the container.

- -
-
-
- ); - } -}); diff --git a/src/components/Setup.react.js b/src/components/Setup.react.js index f54702cb8b..cea965ec32 100644 --- a/src/components/Setup.react.js +++ b/src/components/Setup.react.js @@ -31,7 +31,7 @@ var Setup = React.createClass({
- +
); @@ -47,29 +47,8 @@ var Setup = React.createClass({
-

{this.state.title}

-

{this.state.message}

-
-
-
- - ); - }, - renderCancelled: function () { - return ( -
-
-
-
- {this.renderContents()} -
-
-
-

Setup Cancelled

-

Couldn't Install Requirements

-

Kitematic didn't receive the administrative privileges required to install or upgrade VirtualBox & Docker.

-

Please click retry. If VirtualBox is not installed, you can download & install it manually from the official Oracle website.

-

+

Starting Docker VM

+

To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...

diff --git a/src/menutemplate.js b/src/menutemplate.js index adbf090123..2954323d99 100644 --- a/src/menutemplate.js +++ b/src/menutemplate.js @@ -6,9 +6,8 @@ var util = require('./utils/Util'); var setupUtil = require('./utils/SetupUtil'); var metrics = require('./utils/MetricsUtil'); var machine = require('./utils/DockerMachineUtil'); +var docker = require('./utils/DockerUtil'); var dialog = remote.require('dialog'); -import docker from './utils/DockerUtil'; -import Router from 'react-router'; // main.js var MenuTemplate = function () { @@ -22,7 +21,7 @@ var MenuTemplate = function () { metrics.track('Opened About', { from: 'menu' }); - Router.transitionTo('about'); + router.get().transitionTo('about'); } }, { @@ -36,7 +35,7 @@ var MenuTemplate = function () { metrics.track('Opened Preferences', { from: 'menu' }); - Router.transitionTo('preferences'); + router.get().transitionTo('preferences'); } }, { diff --git a/src/routes.js b/src/routes.js index 3cb21e9cbd..824b5d7069 100644 --- a/src/routes.js +++ b/src/routes.js @@ -14,44 +14,45 @@ import ContainerSettingsVolumes from './components/ContainerSettingsVolumes.reac import ContainerSettingsAdvanced from './components/ContainerSettingsAdvanced.react'; import Preferences from './components/Preferences.react'; import About from './components/About.react'; +import Loading from './components/Loading.react'; import NewContainerSearch from './components/NewContainerSearch.react'; -import NewContainerPull from './components/NewContainerPull.react'; -import {Router, IndexRoute, Route, Link} from 'react-router' +import Router from 'react-router'; + +var Route = Router.Route; +var DefaultRoute = Router.DefaultRoute; +var RouteHandler = Router.RouteHandler; var App = React.createClass({ render: function () { return ( -
- {this.props.children} -
+ ); } }); var routes = ( - - - - - + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + ); diff --git a/src/utils/SetupUtil.js b/src/utils/SetupUtil.js index fccb76ffc8..d78a0e8002 100644 --- a/src/utils/SetupUtil.js +++ b/src/utils/SetupUtil.js @@ -9,16 +9,17 @@ import setupActions from '../actions/SetupActions'; import metrics from './MetricsUtil'; import machine from './DockerMachineUtil'; import docker from './DockerUtil'; +import router from '../router'; let _retryPromise = null; export default { - simulateProgress (estimateSeconds, progress) { + simulateProgress (estimateSeconds) { var times = _.range(0, estimateSeconds * 1000, 200); var timers = []; _.each(times, time => { var timer = setTimeout(() => { - setupActions.progress(100 * time / (estimateSeconds * 1000)); + setupActions.progress({progress: 100 * time / (estimateSeconds * 1000)}); }, time); timers.push(timer); }); @@ -27,7 +28,6 @@ export default { retry (removeVM) { if (removeVM) { machine.rm().finally(() => { - console.log('machine removed'); _retryPromise.resolve(); }); } else { @@ -50,17 +50,23 @@ export default { let exists = await virtualBox.vmExists('default'); if (!exists) { - this.simulateProgress(60, progress => setupActions.progress(progress)); - await machine.rm(); + router.get().transitionTo('setup'); + this.simulateProgress(60); + try { + await machine.rm(); + } catch (err) {} await machine.create(); } else { let state = await machine.state(); - if (state === 'Saved') { - this.simulateProgress(10, progress => setupActions.progress(progress)); - } else { - this.simulateProgress(25, progress => setupActions.progress(progress)); + if (state !== 'Running') { + router.get().transitionTo('setup'); + if (state === 'Saved') { + this.simulateProgress(10); + } else if (state === 'Stopped') { + this.simulateProgress(25) + } + await machine.start(); } - await machine.start(); } let ip = await machine.ip(); @@ -76,6 +82,7 @@ export default { await this.pause(); } } + console.log('Setup finished.'); metrics.track('Setup Finished'); } }; diff --git a/src/utils/VirtualBoxUtil.js b/src/utils/VirtualBoxUtil.js index 20eff5ec08..d5cb1ed996 100644 --- a/src/utils/VirtualBoxUtil.js +++ b/src/utils/VirtualBoxUtil.js @@ -1,18 +1,24 @@ import fs from 'fs'; +import path from 'path'; import util from './Util'; import Promise from 'bluebird'; var VirtualBox = { command: function () { if(util.isWindows()) { - return 'C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe'; + if (process.env.VBOX_MSI_INSTALL_PATH) { + return path.join(process.env.VBOX_MSI_INSTALL_PATH, 'VBoxManage.exe'); + } else { + return path.join(process.env.VBOX_INSTALL_PATH, 'VBoxManage.exe'); + } } else { return '/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'; } }, installed: function () { if(util.isWindows()) { - return fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VBoxManage.exe') && fs.existsSync('C:\\Program Files\\Oracle\\VirtualBox\\VirtualBox.exe'); + return (process.env.VBOX_MSI_INSTALL_PATH && fs.existsSync(path.join(process.env.VBOX_MSI_INSTALL_PATH, 'VBoxManage.exe'))) || + (process.env.VBOX_INSTALL_PATH && fs.existsSync(path.join(process.env.VBOX_INSTALL_PATH, 'VBoxManage.exe'))); } else { return fs.existsSync('/Applications/VirtualBox.app') && fs.existsSync('/Applications/VirtualBox.app/Contents/MacOS/VBoxManage'); } diff --git a/styles/loading.less b/styles/loading.less new file mode 100644 index 0000000000..1d47702b30 --- /dev/null +++ b/styles/loading.less @@ -0,0 +1,13 @@ +.loading { + display: flex; + width: 100%; + height: 100%; + flex-direction: column; + + .loading-content { + display: flex; + flex: 1 1; + align-items: center; + justify-content: center; + } +} diff --git a/styles/main.less b/styles/main.less index 56b0ca58e1..5052298f88 100644 --- a/styles/main.less +++ b/styles/main.less @@ -18,6 +18,7 @@ @import "spinner.less"; @import "animation.less"; @import "container-progress.less"; +@import "loading.less"; html, body { height: 100%; diff --git a/styles/new-container.less b/styles/new-container.less index 30fa2ce62d..396a7f646d 100644 --- a/styles/new-container.less +++ b/styles/new-container.less @@ -292,6 +292,7 @@ } } .card { + flex: 1 0; position: relative; border: 1px solid darken(@gray-lightest, 0%); border-left: 0;