mirror of
https://github.com/docker/docs.git
synced 2026-03-27 14:28:47 +07:00
Better loading indicators, removed some unused code
Signed-off-by: Jeffrey Morgan <jmorganca@gmail.com>
This commit is contained in:
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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/",
|
||||
|
||||
12
src/app.js
12
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(<Router history={history}>{routes}</Router>, document.body)
|
||||
var router = Router.create({
|
||||
routes: routes
|
||||
});
|
||||
router.run(Handler => React.render(<Handler/>, 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', {
|
||||
|
||||
@@ -74,6 +74,7 @@ app.on('ready', function () {
|
||||
mainWindow.webContents.send('application:quitting');
|
||||
return true;
|
||||
});
|
||||
|
||||
app.on('window-all-closed', function() {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
@@ -39,7 +39,6 @@ var Preferences = React.createClass({
|
||||
<div className="item">
|
||||
<RetinaImage src="cartoon-docker.png"/>
|
||||
<h4>Docker Engine</h4>
|
||||
<p>{packages["docker-version"]}</p>
|
||||
</div>
|
||||
<div className="item">
|
||||
<RetinaImage src="cartoon-docker-machine.png"/>
|
||||
|
||||
@@ -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 = (
|
||||
<div className="details-progress">
|
||||
<h2>Waiting For Another Download</h2>
|
||||
<Radial spin="true" progress="90" thick={true} transparent={true}/>
|
||||
<div className="spinner la-ball-clip-rotate la-lg la-dark"><div></div></div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
body = (
|
||||
<div className="details-progress">
|
||||
<h2>Connecting to Docker Hub</h2>
|
||||
<Radial spin="true" progress="90" thick={true} transparent={true}/>
|
||||
<div className="spinner la-ball-clip-rotate la-lg la-dark"><div></div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className="containers">
|
||||
<Header />
|
||||
@@ -158,7 +160,7 @@ var Containers = React.createClass({
|
||||
<section className={sidebarHeaderClass}>
|
||||
<h4>Containers</h4>
|
||||
<div className="create">
|
||||
<Router.Link to="new">
|
||||
<Router.Link to="search">
|
||||
<span className="btn btn-new btn-action has-icon btn-hollow"><span className="icon icon-add"></span>New</span>
|
||||
</Router.Link>
|
||||
</div>
|
||||
|
||||
15
src/components/Loading.react.js
Normal file
15
src/components/Loading.react.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react/addons';
|
||||
import Header from './Header.react';
|
||||
|
||||
module.exports = React.createClass({
|
||||
render: function () {
|
||||
return (
|
||||
<div className="loading">
|
||||
<Header hideLogin={true}/>
|
||||
<div className="loading-content">
|
||||
<div className="spinner la-ball-clip-rotate la-lg la-dark"><div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 (
|
||||
<div className="details">
|
||||
<div className="new-container-pull">
|
||||
<div className="content">
|
||||
<h1>You're about to download and run <a onClick={this.handleOpenClick}>{this.props.pending.repo}:{this.props.pending.tag}</a>.</h1>
|
||||
<h1>Please confirm to create the container.</h1>
|
||||
<div className="buttons">
|
||||
<a className="btn btn-action" onClick={this.handleCancelClick}>Cancel</a> <a onClick={this.handleConfirmClick} className="btn btn-action">Confirm</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -31,7 +31,7 @@ var Setup = React.createClass({
|
||||
<div className="contents">
|
||||
<RetinaImage src="boot2docker.png" checkIfRetinaImgExists={false}/>
|
||||
<div className="detail">
|
||||
<Radial progress={this.state.progress} thick={true} gray={true}/>
|
||||
<Radial progress={Math.round(this.state.progress)} thick={true} gray={true}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -47,29 +47,8 @@ var Setup = React.createClass({
|
||||
</div>
|
||||
<div className="desc">
|
||||
<div className="content">
|
||||
<h1>{this.state.title}</h1>
|
||||
<p>{this.state.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
renderCancelled: function () {
|
||||
return (
|
||||
<div className="setup">
|
||||
<Header hideLogin={true}/>
|
||||
<div className="setup-content">
|
||||
<div className="image">
|
||||
{this.renderContents()}
|
||||
</div>
|
||||
<div className="desc">
|
||||
<div className="content">
|
||||
<h4>Setup Cancelled</h4>
|
||||
<h1>Couldn't Install Requirements</h1>
|
||||
<p>Kitematic didn't receive the administrative privileges required to install or upgrade VirtualBox & Docker.</p>
|
||||
<p>Please click retry. If VirtualBox is not installed, you can download & install it manually from the <a onClick={this.handleOpenWebsite}>official Oracle website</a>.</p>
|
||||
<p><button className="btn btn-action" onClick={this.handleCancelRetry}>Retry</button></p>
|
||||
<h1>Starting Docker VM</h1>
|
||||
<p>To run Docker containers on your computer, Kitematic is starting a Linux virtual machine. This may take a minute...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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 (
|
||||
<div>
|
||||
{this.props.children}
|
||||
</div>
|
||||
<RouteHandler/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var routes = (
|
||||
<Route path="/" component={App}>
|
||||
<IndexRoute component={Setup}/>
|
||||
<Route path="account" component={Account}>
|
||||
<Route path="signup" component={AccountSignup}/>
|
||||
<Route path="login" component={AccountLogin}/>
|
||||
<Route name="app" path="/" handler={App}>
|
||||
<Route name="account" path="account" handler={Account}>
|
||||
<Route name="signup" path="signup" handler={AccountSignup}/>
|
||||
<Route name="login" path="login" handler={AccountLogin}/>
|
||||
</Route>
|
||||
<Route path="containers" component={Containers}>
|
||||
<Route path="details/:name" component={ContainerDetails}>
|
||||
<IndexRoute component={ContainerHome} />
|
||||
<Route path="logs" component={ContainerLogs}/>
|
||||
<Route path="settings" component={ContainerSettings}>
|
||||
<Route path="general" component={ContainerSettingsGeneral}/>
|
||||
<Route path="ports" component={ContainerSettingsPorts}/>
|
||||
<Route path="volumes" component={ContainerSettingsVolumes}/>
|
||||
<Route path="advanced" component={ContainerSettingsAdvanced}/>
|
||||
<Route name="containers" path="containers" handler={Containers}>
|
||||
<Route name="container" path="details/:name" handler={ContainerDetails}>
|
||||
<DefaultRoute name="containerHome" handler={ContainerHome} />
|
||||
<Route name="containerLogs" path="logs" handler={ContainerLogs}/>
|
||||
<Route name="containerSettings" path="settings" handler={ContainerSettings}>
|
||||
<Route name="containerSettingsGeneral" path="general" handler={ContainerSettingsGeneral}/>
|
||||
<Route name="containerSettingsPorts" path="ports" handler={ContainerSettingsPorts}/>
|
||||
<Route name="containerSettingsVolumes" path="volumes" handler={ContainerSettingsVolumes}/>
|
||||
<Route name="containerSettingsAdvanced" path="advanced" handler={ContainerSettingsAdvanced}/>
|
||||
</Route>
|
||||
</Route>
|
||||
<Route name="new" path="new">
|
||||
<IndexRoute component={NewContainerSearch}/>
|
||||
</Route>
|
||||
<Route path="preferences" component={Preferences}/>
|
||||
<Route path="about" component={About}/>
|
||||
<Route name="search" handler={NewContainerSearch}/>
|
||||
<Route name="preferences" path="preferences" handler={Preferences}/>
|
||||
<Route name="about" path="about" handler={About}/>
|
||||
</Route>
|
||||
<DefaultRoute name="loading" handler={Loading}/>
|
||||
<Route name="setup" handler={Setup}/>
|
||||
</Route>
|
||||
);
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
13
styles/loading.less
Normal file
13
styles/loading.less
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
@import "spinner.less";
|
||||
@import "animation.less";
|
||||
@import "container-progress.less";
|
||||
@import "loading.less";
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
}
|
||||
}
|
||||
.card {
|
||||
flex: 1 0;
|
||||
position: relative;
|
||||
border: 1px solid darken(@gray-lightest, 0%);
|
||||
border-left: 0;
|
||||
|
||||
Reference in New Issue
Block a user