diff --git a/__mocks__/electron.js b/__mocks__/electron.js
index 6653cb6b..6b6219a3 100644
--- a/__mocks__/electron.js
+++ b/__mocks__/electron.js
@@ -2,6 +2,7 @@ module.exports = {
app: jest.fn(),
ipcRenderer: {
send: jest.fn(),
+ on: jest.fn(),
},
remote: {
require: jest.fn(name => {
diff --git a/app.js b/app.js
index 09788889..7123f1da 100644
--- a/app.js
+++ b/app.js
@@ -11,6 +11,7 @@ const omit = require('lodash').omit;
// Electron Libs
const { app, BrowserWindow, ipcMain } = require('electron');
+const { autoUpdater } = require('electron-updater');
// Place a BrowserWindow in center of primary display
const centerOnPrimaryDisplay = require('./app/helpers/center-on-primary-display');
@@ -98,7 +99,6 @@ function createMainWindow() {
slashes: true,
})
);
-
// Add Event Listeners
mainWindow.on('show', event => {
if (isDev) mainWindow.webContents.openDevTools({ mode: 'detach' });
@@ -294,9 +294,19 @@ function addEventListeners() {
ipcMain.on('quit-app', () => {
app.quit();
});
- // Use with autoUpdater
- ipcMain.on('restart-app', () => {
- app.relaunch();
+ // Quit and install
+ // https://github.com/electron-userland/electron-builder/issues/1604#issuecomment-306709572
+ ipcMain.on('quit-and-install', () => {
+ setImmediate(() => {
+ // Remove this listener
+ app.removeAllListeners("window-all-closed");
+ // Force close all windows
+ tourWindow.destroy();
+ mainWindow.destroy();
+ previewWindow.destroy();
+ // Start the quit and update sequence
+ autoUpdater.quitAndInstall(false);
+ })
});
}
@@ -371,9 +381,11 @@ function initialize() {
});
// Close all windows before quit the app
app.on('before-quit', () => {
- tourWindow.destroy();
- mainWindow.destroy();
- previewWindow.destroy();
+ // Use condition in case quit sequence is initiated by autoUpdater
+ // which will destroy all there windows already before emitting this event
+ if (tourWindow !== null) tourWindow.destroy();
+ if (mainWindow !== null) mainWindow.destroy();
+ if (previewWindow !== null) previewWindow.destroy();
});
console.timeEnd('init');
}
diff --git a/app/App.jsx b/app/App.jsx
index 7e856831..33a5c12b 100644
--- a/app/App.jsx
+++ b/app/App.jsx
@@ -97,7 +97,6 @@ class App extends PureComponent {
-
);
}
diff --git a/app/components/layout/AppNav.jsx b/app/components/layout/AppNav.jsx
index 39a497d6..69a7e4ef 100644
--- a/app/components/layout/AppNav.jsx
+++ b/app/components/layout/AppNav.jsx
@@ -49,7 +49,7 @@ export const SideBar = styled.div`
position: relative;
display: flex;
flex-direction: column;
- justify-content: flex-start;
+ justify-content: space-between;
height: 100%;
width: 80px;
min-width: 80px;
@@ -95,6 +95,8 @@ export const ActiveIndicator = styled.div`
}
`;
+import AppUpdate from './AppUpdate';
+
function AppNav({ activeTab, changeTab }) {
const marginTopValue = setMarginValue(activeTab);
const allTabsComponent = allTabs.map(tab => (
@@ -104,19 +106,22 @@ function AppNav({ activeTab, changeTab }) {
));
return (
-
- {({ marginTop }) => (
-
-
-
- )}
-
- {allTabsComponent}
+
+
+ {({ marginTop }) => (
+
+
+
+ )}
+
+ {allTabsComponent}
+
+
);
}
diff --git a/app/components/layout/AppUpdate.jsx b/app/components/layout/AppUpdate.jsx
index d933f91a..ef46dfbf 100644
--- a/app/components/layout/AppUpdate.jsx
+++ b/app/components/layout/AppUpdate.jsx
@@ -1,62 +1,52 @@
// Libraries
-import React, { Component } from 'react';
-const ipc = require('electron').ipcRenderer;
+import React, { PureComponent } from 'react';
import openDialog from '../../renderers/dialog';
+import { Circle } from 'rc-progress';
+const ipc = require('electron').ipcRenderer
-// Styles
-import styled from 'styled-components';
+// Styled Components
+import styled, { keyframes } from 'styled-components';
-const Wrapper = styled.div`
- position: fixed;
- height: auto;
- bottom: 0;
- width: 100%;
- padding-left: 120px;
- padding-right: 40px;
-`;
-
-const Message = styled.p`
- font-size: 12px;
- letter-spacing: 0.5px;
- color: white;
- margin-bottom: 0px;
- line-height: 1.75;
+const breathing = keyframes`
+ 0% { opacity: 0.5; }
+ 100% { opacity: 1; }
`;
-const Content = styled.div`
- background: #469fe5;
- border-radius: 4px 4px 0 0;
- padding: 10px 20px;
- border: 1px solid rgba(0, 0, 0, 0.1);
- text-transform: uppercase;
- ${props => props.type === 'error' && `background: #EC476E;`};
- ${props => props.type === 'success' && `background: #6BBB69;`};
+const Indicator = styled.div`
display: flex;
- align-items: flex-start;
- justify-content: space-between;
- button {
- color: white;
+ align-items: center;
+ justify-content: center;
+ height: 60px;
+ width: 100%;
+ color: #f2f3f4;
+ i {
+ font-size: 24px;
+ animation: ${breathing} 1s infinite alternate;
+ }
+ svg {
+ width: 24px;
+ height: 24px;
}
`;
-import Button from '../../components/shared/Button';
-
-class AppUpdate extends Component {
+class AppUpdate extends PureComponent {
constructor(props) {
super(props);
this.state = {
- message: null,
- type: 'info',
+ checking: false,
+ downloading: false,
+ progress: null,
};
- this.removeMessage = this.removeMessage.bind(this);
+ this.hideIndicator = this.hideIndicator.bind(this);
}
componentDidMount() {
- ipc.on('update-checking', event => {
- this.setState({ message: 'Checking for update' });
+ ipc.on('update-checking', () => {
+ this.setState({ checking: true });
});
ipc.on('update-available', () => {
+ this.hideIndicator();
openDialog(
{
type: 'info',
@@ -74,7 +64,7 @@ class AppUpdate extends Component {
title: 'No Updates',
message: 'Current version is up-to-date',
});
- this.removeMessage();
+ this.hideIndicator();
});
ipc.on('update-error', (event, error) => {
@@ -83,36 +73,33 @@ class AppUpdate extends Component {
title: 'Update Error',
message: error,
});
- this.removeMessage();
+ this.hideIndicator();
});
ipc.on('update-download-confirmed', (event, index) => {
- // Start the download
- if (index === 0) {
- this.setState({
- message: 'Downloading update ...',
- type: 'success',
- });
- ipc.send('update-download-started');
- }
// Cancel the download
if (index === 1) {
- this.removeMessage();
+ this.hideIndicator();
+ return;
+ }
+ // Start the download
+ if (index === 0) {
+ this.setState(
+ {
+ downloading: true,
+ },
+ ipc.send('update-download-started')
+ );
}
});
- ipc.on('update-download-progress', (event, progressMessage) => {
+ ipc.on('update-download-progress', (event, percentage) => {
this.setState({
- message: progressMessage,
+ progress: percentage,
});
});
ipc.on('update-downloaded', () => {
- // Update Message
- this.setState({
- message: 'Download Completed!',
- });
- // Ask user to upgrade now or later
openDialog(
{
type: 'info',
@@ -122,19 +109,16 @@ class AppUpdate extends Component {
},
'upgrade-confirmed'
);
+ this.hideIndicator();
});
ipc.on('upgrade-confirmed', (event, index) => {
if (index === 0) {
- ipc.send('restart-app');
+ ipc.send('quit-and-install');
}
});
}
- shouldComponentUpdate(nextProps, nextState) {
- return this.state !== nextState;
- }
-
componentWillUnmount() {
ipc.removeAllListeners([
'update-checking',
@@ -148,25 +132,29 @@ class AppUpdate extends Component {
]);
}
- removeMessage() {
+ hideIndicator() {
this.setState({
- message: null,
- type: 'info',
+ checking: false,
+ downloading: false,
+ progress: null,
});
}
render() {
- const { message, type } = this.state;
- return message ? (
-
-
- {message}
-
-
-
- ) : null;
+ return (
+
+ {this.state.checking && }
+ {this.state.downloading && (
+
+ )}
+
+ );
}
}
diff --git a/app/components/layout/__tests__/AppNav.spec.js b/app/components/layout/__tests__/AppNav.spec.js
index 86cf70c3..bfe4450b 100644
--- a/app/components/layout/__tests__/AppNav.spec.js
+++ b/app/components/layout/__tests__/AppNav.spec.js
@@ -15,7 +15,7 @@ describe('Renders correctly to the DOM', () => {
});
it('renders 4 tabs', () => {
- // expect(wrapper.find('a')).toHaveLength(4);
+ expect(wrapper.find('a')).toHaveLength(4);
});
it('has correct porps', () => {
@@ -23,9 +23,9 @@ describe('Renders correctly to the DOM', () => {
// expect(wrapper.prop('activeTab')).not.toEqual('settings');
});
- // it('contains active indicator', () => {
+ it('contains active indicator', () => {
// const sideBar = shallow();
// expect(wrapper.contains(sideBar)).toEqual(true);
- // });
- // it('shows active indicator at the correct position');
+ });
+ it('shows active indicator at the correct position');
});
diff --git a/main/updater.js b/main/updater.js
index 91982577..c6297e3f 100644
--- a/main/updater.js
+++ b/main/updater.js
@@ -7,13 +7,19 @@ const isDev = require('electron-is-dev');
// Disable Auto Downloading update;
autoUpdater.autoDownload = false;
+// Check for update silently
+let silentMode = true;
+
// Set mainWindow
const mainWindowID = appConfig.get('mainWindowID');
const mainWindow = BrowserWindow.fromId(mainWindowID);
// Check for Updates
-ipcMain.on('check-for-updates', event => {
- if(!isDev) checkForUpdate();
+ipcMain.on('check-for-updates', (event) => {
+ // Turn off silent mode
+ silentMode = false;
+ // if(!isDev) checkForUpdate();
+ checkForUpdate();
});
// Start Download
@@ -25,7 +31,10 @@ ipcMain.on('update-download-started', () => {
// ====================================
// Checking for Update
autoUpdater.on('checking-for-update', () => {
- mainWindow.send('update-checking');
+ // Only notice user when they checked manually
+ if (!silentMode) {
+ mainWindow.send('update-checking');
+ }
});
// Update Available
@@ -35,7 +44,10 @@ autoUpdater.on('update-available', info => {
// Update Not Available
autoUpdater.on('update-not-available', () => {
- mainWindow.send('update-not-available');
+ // Only notice user when they checked manually
+ if (!silentMode) {
+ mainWindow.send('update-not-available');
+ }
});
// Update Error
@@ -51,8 +63,7 @@ autoUpdater.on('error', error => {
// Download Progress
autoUpdater.on('download-progress', progressObj => {
- const message = `Downloaded ${progressObj.percent} %`;
- mainWindow.send('update-download-progress', message);
+ mainWindow.send('update-download-progress', progressObj.percent);
});
// Update Downloaded
diff --git a/package.json b/package.json
index ec0e44da..8ff91de1 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"jimp": "^0.2.28",
"moment": "^2.20.1",
"pouchdb-browser": "6.2.0",
+ "rc-progress": "^2.2.5",
"react": "^16.2.0",
"react-addons-shallow-compare": "^15.6.2",
"react-beautiful-dnd": "^2.4.1",
diff --git a/yarn.lock b/yarn.lock
index 2893827c..b4d61124 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1199,7 +1199,7 @@ babel-register@^6.26.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"
-babel-runtime@6.26.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@6.26.0, babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
@@ -6542,6 +6542,13 @@ raw-body@2.3.2:
iconv-lite "0.4.19"
unpipe "1.0.0"
+rc-progress@^2.2.5:
+ version "2.2.5"
+ resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.2.5.tgz#e61d0544bf9d4208e5ba32fc50962159e7f952a3"
+ dependencies:
+ babel-runtime "6.x"
+ prop-types "^15.5.8"
+
rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.1.7, rc@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"