diff --git a/.babelrc b/.babelrc
index 1cc3c74..c954966 100755
--- a/.babelrc
+++ b/.babelrc
@@ -6,12 +6,5 @@
],
"plugins": [
"transform-class-properties"
- ],
- "env": {
- "development": {
- "plugins": [
- "react-hot-loader/babel"
- ]
- }
- }
+ ]
}
diff --git a/.compilerc b/.compilerc
index fdcaf74..8cd9b78 100755
--- a/.compilerc
+++ b/.compilerc
@@ -10,9 +10,6 @@
],
"env": {
"development": {
- "plugins": [
- "react-hot-loader/babel"
- ],
"sourceMaps": "both"
},
"production": {
diff --git a/app/App.js b/app/App.js
index a0959f8..c2e11e9 100755
--- a/app/App.js
+++ b/app/App.js
@@ -1,7 +1,6 @@
import React, { Component } from 'react';
import { HashRouter } from 'react-router-dom';
-import { hot } from 'react-hot-loader';
import { LocaleProvider } from './components/antd';
import MainLayout from './layouts/MainLayout';
@@ -22,4 +21,4 @@ class App extends Component {
}
-export default hot(module)(App);
+export default App;
diff --git a/app/components/TableProcesses.js b/app/components/TableProcesses.js
index 2a979ba..3064f73 100644
--- a/app/components/TableProcesses.js
+++ b/app/components/TableProcesses.js
@@ -114,7 +114,7 @@ class ColumnSave extends PureComponent {
render() {
const { save } = this.state;
return (
- {save.toFixed(1)} %
+ {save.toFixed(1)}%
);
}
diff --git a/app/index.js b/app/index.js
index 8b83d2f..c080b20 100755
--- a/app/index.js
+++ b/app/index.js
@@ -15,11 +15,24 @@ document.addEventListener('dragover', e => {
const React = require('react');
const ReactDOM = require('react-dom');
+const { AppContainer } = require('react-hot-loader');
require('./styles');
-const App = require('./App').default;
-ReactDOM.render(
- ,
- document.getElementById('root'),
-);
+const render = Comp => {
+ const NextApp = require('./App').default;
+ ReactDOM.render(
+
+
+ ,
+ document.getElementById('root'),
+ );
+};
+
+render();
+
+if (module.hot) {
+ module.hot.accept('./App', () => {
+ render();
+ });
+}
diff --git a/app/package.json b/app/package.json
index a41056d..efa1c8e 100644
--- a/app/package.json
+++ b/app/package.json
@@ -1,7 +1,7 @@
{
"name": "emage",
"productName": "E-Mage",
- "version": "0.0.2",
+ "version": "1.0.0",
"description": "From developers to developers: a cross-platform tool to help with image compression.",
"main": "./main.prod.js",
"author": {
diff --git a/app/pages/HomePage.js b/app/pages/HomePage.js
index 218b9b0..588a3fe 100755
--- a/app/pages/HomePage.js
+++ b/app/pages/HomePage.js
@@ -1,5 +1,6 @@
-import React, { Component } from 'react';
+import React, { PureComponent } from 'react';
+import styles from './HomePage.scss';
import {
Upload, Icon, Card,
Row, Col,
@@ -9,6 +10,7 @@ import TableProcesses from '../components/TableProcesses';
import Process from '../utils/Process';
import { arrayPush } from '../utils/arrays';
import { isOS } from '../utils/platform';
+import { formatBytes } from '../utils/formatter';
const { Dragger } = Upload;
@@ -57,7 +59,7 @@ const getOptions = (fileType, state) => {
return undefined;
};
-export default class HomePage extends Component {
+export default class HomePage extends PureComponent {
constructor(props) {
super(props);
@@ -68,9 +70,21 @@ export default class HomePage extends Component {
svgOptions: SVG_OPTIONS.map(o => o.value),
gifOptions: GIF_OPTIONS.map(o => o.value),
processes: [],
+ total: {
+ originalSize: 0,
+ size: 0,
+ savedSum: 0,
+ savedAvg: 0,
+ savedMax: 0,
+ },
};
}
+ componentWillUnmount() {
+ const { processes } = this.state;
+ processes.forEach(p => p.removeListener('end', this._updateTotal));
+ }
+
_onUploadChange = file => {
if (Array.isArray(file)) {
file.forEach(this._compress);
@@ -92,9 +106,11 @@ export default class HomePage extends Component {
if (!Array.isArray(selectedOptions)) {
return null;
}
+ const process = new Process(file, selectedOptions);
+ process.on('end', this._updateTotal);
return {
processes: [
- new Process(file, selectedOptions),
+ process,
...processes,
],
};
@@ -117,6 +133,32 @@ export default class HomePage extends Component {
this.setState({ svgOptions: checkedValues });
}
+ _updateTotal = () => {
+ const { processes } = this.state;
+ const total = processes.reduce(
+ (result, process) => {
+ if (!process.isFinished()) {
+ return result;
+ }
+ const newResult = {
+ originalSize: result.originalSize + process.getOriginalSize(),
+ size: result.size + process.getSize(),
+ savedSum: result.savedSum + process.getSave(),
+ savedMax: Math.max(result.savedMax, process.getSave()),
+ };
+ newResult.savedAvg = newResult.savedSum / processes.length;
+ return newResult;
+ }, {
+ originalSize: 0,
+ size: 0,
+ savedSum: 0,
+ savedAvg: 0,
+ savedMax: 0,
+ },
+ );
+ this.setState({ total });
+ }
+
_renderOptions = () => {
const {
jpgOptions, pngOptions,
@@ -164,6 +206,26 @@ export default class HomePage extends Component {
);
}
+ _renderTotal = () => {
+ const { total } = this.state;
+ const {
+ originalSize, size,
+ savedAvg, savedMax,
+ } = total;
+ if (!originalSize) return null;
+ return (
+
+
+ Saved {formatBytes(originalSize - size)} out of {formatBytes(originalSize)}.
+
+ {' '}
+
+ {savedAvg.toFixed(1)}% per file on avarage (up to {savedMax.toFixed(1)}%).
+
+
+ );
+ }
+
render() {
const { processes } = this.state;
return (
@@ -185,7 +247,9 @@ export default class HomePage extends Component {
- Click or drag images to this area to optimize
+
+ Click or drag images to this area to optimize
+
Support for a single or bulk upload.
@@ -194,6 +258,7 @@ export default class HomePage extends Component {
3. See the magic!
+ {this._renderTotal()}
);
diff --git a/app/pages/HomePage.scss b/app/pages/HomePage.scss
index e69de29..b458602 100755
--- a/app/pages/HomePage.scss
+++ b/app/pages/HomePage.scss
@@ -0,0 +1,3 @@
+.totalContainer {
+ margin-top: 8px;
+}
diff --git a/app/styles/default.less b/app/styles/default.less
index 8afb618..f6d3323 100644
--- a/app/styles/default.less
+++ b/app/styles/default.less
@@ -12,3 +12,5 @@
@import "~antd/lib/table/style/index.less";
@import "~antd/lib/tooltip/style/index.less";
@import "~antd/lib/upload/style/index.less";
+
+@primary-color: #ffc107;
diff --git a/app/utils/formatter.js b/app/utils/formatter.js
new file mode 100644
index 0000000..5a9cdc0
--- /dev/null
+++ b/app/utils/formatter.js
@@ -0,0 +1,19 @@
+
+export const formatBytes = bytes => {
+ let converted = parseFloat(bytes);
+ if (converted < 1024) {
+ return `${converted.toFixed(2)} bytes`;
+ }
+ converted /= 1024.0;
+ if (converted < 1024) {
+ return `${converted.toFixed(2)} KB`;
+ }
+ converted /= 1024.0;
+ if (converted < 1024) {
+ return `${converted.toFixed(2)} MB`;
+ }
+ converted /= 1024.0;
+ return `${converted.toFixed(2)} GB`;
+};
+
+export default {};
diff --git a/webpack.config.renderer.dev.js b/webpack.config.renderer.dev.js
index b7983f5..2ea0e77 100644
--- a/webpack.config.renderer.dev.js
+++ b/webpack.config.renderer.dev.js
@@ -47,6 +47,7 @@ export default merge.smart(baseConfig, {
entry: [
'babel-polyfill',
+ 'react-hot-loader/patch',
`webpack-dev-server/client?http://localhost:${port}/`,
'webpack/hot/only-dev-server',
path.join(__dirname, 'app/index.js'),