summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gui/flow-libs/electron.js.flow1
-rw-r--r--gui/packages/desktop/src/renderer/app.js70
-rw-r--r--gui/packages/desktop/src/renderer/components/NotificationArea.js4
-rw-r--r--gui/packages/desktop/src/renderer/components/NotificationBanner.js1
-rw-r--r--gui/packages/desktop/src/renderer/lib/notification-controller.js47
-rw-r--r--gui/packages/desktop/src/renderer/redux/version/actions.js9
-rw-r--r--gui/packages/desktop/src/renderer/redux/version/reducers.js36
7 files changed, 119 insertions, 49 deletions
diff --git a/gui/flow-libs/electron.js.flow b/gui/flow-libs/electron.js.flow
index b0facb6c7e..bc37e5a524 100644
--- a/gui/flow-libs/electron.js.flow
+++ b/gui/flow-libs/electron.js.flow
@@ -57,6 +57,7 @@ declare module 'electron' {
declare class Remote {
app: App;
+ shell: Shell;
getCurrentWindow(): BrowserWindow;
getCurrentWebContents(): WebContents;
getGlobal(name: string): ?mixed;
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js
index d4f5f76830..c465e74b8d 100644
--- a/gui/packages/desktop/src/renderer/app.js
+++ b/gui/packages/desktop/src/renderer/app.js
@@ -360,18 +360,78 @@ export default class AppRenderer {
actions.settings.updateAutoConnect(autoConnect);
}
+ async _getAppComponentsVersions() {
+ const daemonVersion = await this._daemonRpc.getCurrentVersion();
+ const guiVersion = remote.app.getVersion().replace('.0', '');
+ return {
+ daemon: daemonVersion,
+ gui: guiVersion,
+ isConsistent: daemonVersion === guiVersion,
+ };
+ }
+
async _fetchCurrentVersion() {
const actions = this._reduxActions;
- const versionFromDaemon = await this._daemonRpc.getCurrentVersion();
- const versionFromGui = remote.app.getVersion().replace('.0', '');
+ const versions = await this._getAppComponentsVersions();
+
+ // notify user about inconsistent version
+ if (process.env.NODE_ENV !== 'development' && !versions.isConsistent) {
+ this._notificationController.notifyInconsistentVersion();
+ }
- actions.version.updateVersion(versionFromDaemon, versionFromDaemon === versionFromGui);
+ actions.version.updateVersion(versions.gui, versions.isConsistent);
}
async _fetchLatestVersionInfo() {
+ function isBeta(version: string) {
+ return version.includes('-');
+ }
+
+ function nextUpgrade(current: string, latest: string, latestStable: string): ?string {
+ if (isBeta(current)) {
+ return current === latest ? null : latest;
+ } else {
+ return current === latestStable ? null : latestStable;
+ }
+ }
+
+ function checkIfLatest(current: string, latest: string, latestStable: string): boolean {
+ // perhaps -beta?
+ if (isBeta(current)) {
+ return current === latest;
+ } else {
+ // must be stable
+ return current === latestStable;
+ }
+ }
+
+ const versions = await this._getAppComponentsVersions();
+ const versionInfo = await this._daemonRpc.getVersionInfo();
+ const latestVersion = versionInfo.latest.latest;
+ const latestStableVersion = versionInfo.latest.latestStable;
+
+ // the reason why we rely on daemon version here is because daemon obtains the version info
+ // based on its built-in version information
+ const isUpToDate = checkIfLatest(versions.daemon, latestVersion, latestStableVersion);
+ const upgradeVersion = nextUpgrade(versions.daemon, latestVersion, latestStableVersion);
+
+ // notify user to update the app if it became unsupported
+ if (
+ process.env.NODE_ENV !== 'development' &&
+ versions.isConsistent &&
+ !versionInfo.currentIsSupported &&
+ upgradeVersion
+ ) {
+ this._notificationController.notifyUnsupportedVersion(upgradeVersion);
+ }
+
// fetching the latest version info has a higher latency because the daemon communicates with
// the API server
- this._reduxActions.version.updateLatest(await this._daemonRpc.getVersionInfo());
+ this._reduxActions.version.updateLatest({
+ ...versionInfo,
+ nextUpgrade: upgradeVersion,
+ upToDate: isUpToDate,
+ });
}
async _onOpenConnection() {
@@ -535,7 +595,7 @@ export default class AppRenderer {
this._updateConnectionStatus(tunnelState);
this._updateUserLocation(tunnelState.state);
this._updateTrayIcon(tunnelState.state);
- this._notificationController.notify(tunnelState);
+ this._notificationController.notifyTunnelState(tunnelState);
}
_setSettings(newSettings: Settings) {
diff --git a/gui/packages/desktop/src/renderer/components/NotificationArea.js b/gui/packages/desktop/src/renderer/components/NotificationArea.js
index e469d657a4..8fff84f375 100644
--- a/gui/packages/desktop/src/renderer/components/NotificationArea.js
+++ b/gui/packages/desktop/src/renderer/components/NotificationArea.js
@@ -138,9 +138,9 @@ export default class NotificationArea extends Component<Props, State> {
<NotificationIndicator type={'error'} />
<NotificationContent>
<NotificationTitle>{'UNSUPPORTED VERSION'}</NotificationTitle>
- <NotificationSubtitle>{`This app version might have security issues. Please upgrade to ${
+ <NotificationSubtitle>{`You are running an unsupported app version. Please upgrade to ${
this.state.upgradeVersion
- }`}</NotificationSubtitle>
+ } now to ensure your security`}</NotificationSubtitle>
</NotificationContent>
<NotificationActions>
<NotificationOpenLinkAction
diff --git a/gui/packages/desktop/src/renderer/components/NotificationBanner.js b/gui/packages/desktop/src/renderer/components/NotificationBanner.js
index 03f0247e88..a168ee0bf7 100644
--- a/gui/packages/desktop/src/renderer/components/NotificationBanner.js
+++ b/gui/packages/desktop/src/renderer/components/NotificationBanner.js
@@ -46,6 +46,7 @@ const styles = {
flex: 0,
flexDirection: 'column',
justifyContent: 'center',
+ marginLeft: 5,
}),
actionButton: Styles.createButtonStyle({
flex: 1,
diff --git a/gui/packages/desktop/src/renderer/lib/notification-controller.js b/gui/packages/desktop/src/renderer/lib/notification-controller.js
index 9f53524295..9cb2f925dc 100644
--- a/gui/packages/desktop/src/renderer/lib/notification-controller.js
+++ b/gui/packages/desktop/src/renderer/lib/notification-controller.js
@@ -2,28 +2,30 @@
import { remote } from 'electron';
import log from 'electron-log';
+import config from '../../config';
import type { TunnelStateTransition } from './daemon-rpc';
export default class NotificationController {
_activeNotification: ?Notification;
_reconnecting = false;
+ _presentedNotifications = {};
- notify(tunnelState: TunnelStateTransition) {
+ notifyTunnelState(tunnelState: TunnelStateTransition) {
switch (tunnelState.state) {
case 'connecting':
if (!this._reconnecting) {
- this._show('Connecting');
+ this._showTunnelStateNotification('Connecting');
}
break;
case 'connected':
- this._show('Secured');
+ this._showTunnelStateNotification('Secured');
break;
case 'disconnected':
- this._show('Unsecured');
+ this._showTunnelStateNotification('Unsecured');
break;
case 'blocked':
- this._show('Blocked all connections');
+ this._showTunnelStateNotification('Blocked all connections');
break;
case 'disconnecting':
switch (tunnelState.details) {
@@ -32,7 +34,7 @@ export default class NotificationController {
// no-op
break;
case 'reconnect':
- this._show('Reconnecting');
+ this._showTunnelStateNotification('Reconnecting');
this._reconnecting = true;
return;
}
@@ -44,7 +46,29 @@ export default class NotificationController {
this._reconnecting = false;
}
- _show(message: string) {
+ notifyInconsistentVersion() {
+ this._presentNotificationOnce('inconsistent-version', () => {
+ new Notification(remote.app.getName(), {
+ body: 'Inconsistent internal version information, please restart the app',
+ silent: true,
+ });
+ });
+ }
+
+ notifyUnsupportedVersion(upgradeVersion: string) {
+ this._presentNotificationOnce('unsupported-version', () => {
+ const notification = new Notification(remote.app.getName(), {
+ body: `You are running an unsupported app version. Please upgrade to ${upgradeVersion} now to ensure your security`,
+ silent: true,
+ });
+
+ notification.addEventListener('click', () => {
+ remote.shell.openExternal(config.links.download);
+ });
+ });
+ }
+
+ _showTunnelStateNotification(message: string) {
const lastNotification = this._activeNotification;
const sameAsLastNotification = lastNotification && lastNotification.body === message;
@@ -59,6 +83,7 @@ export default class NotificationController {
newNotification.addEventListener('show', () => {
// If the notification is closed too soon, it might still get shown. If that happens, close()
// should be called again so that it is closed immediately.
+ // Tracking issue: https://github.com/electron/electron/issues/12887
if (this._activeNotification !== newNotification) {
newNotification.close();
}
@@ -68,4 +93,12 @@ export default class NotificationController {
lastNotification.close();
}
}
+
+ _presentNotificationOnce(notificationName: string, presentNotification: () => void) {
+ const presented = this._presentedNotifications;
+ if (!presented[notificationName]) {
+ presented[notificationName] = true;
+ presentNotification();
+ }
+ }
}
diff --git a/gui/packages/desktop/src/renderer/redux/version/actions.js b/gui/packages/desktop/src/renderer/redux/version/actions.js
index 0277a7f069..4eee3fcc61 100644
--- a/gui/packages/desktop/src/renderer/redux/version/actions.js
+++ b/gui/packages/desktop/src/renderer/redux/version/actions.js
@@ -2,9 +2,14 @@
import type { AppVersionInfo } from '../../lib/daemon-rpc';
+type UpdateLatestActionPayload = {
+ upToDate: boolean,
+ nextUpgrade: ?string,
+} & AppVersionInfo;
+
export type UpdateLatestAction = {
type: 'UPDATE_LATEST',
- latestInfo: AppVersionInfo,
+ latestInfo: UpdateLatestActionPayload,
};
export type UpdateVersionAction = {
@@ -15,7 +20,7 @@ export type UpdateVersionAction = {
export type VersionAction = UpdateLatestAction | UpdateVersionAction;
-function updateLatest(latestInfo: AppVersionInfo): UpdateLatestAction {
+function updateLatest(latestInfo: UpdateLatestActionPayload): UpdateLatestAction {
return {
type: 'UPDATE_LATEST',
latestInfo,
diff --git a/gui/packages/desktop/src/renderer/redux/version/reducers.js b/gui/packages/desktop/src/renderer/redux/version/reducers.js
index c3d6a07382..a2bc07c3e7 100644
--- a/gui/packages/desktop/src/renderer/redux/version/reducers.js
+++ b/gui/packages/desktop/src/renderer/redux/version/reducers.js
@@ -22,45 +22,17 @@ const initialState: VersionReduxState = {
consistent: true,
};
-function isBeta(version: string) {
- return version.includes('-');
-}
-
-function nextUpgrade(current: string, latest: ?string, latestStable: ?string): ?string {
- if (isBeta(current)) {
- return current === latest ? null : latest;
- } else {
- return current === latestStable ? null : latestStable;
- }
-}
-
-function checkIfLatest(current: string, latest: ?string, latestStable: ?string): boolean {
- // perhaps -beta?
- if (isBeta(current)) {
- return current === latest || latest === null;
- } else {
- // must be stable
- return current === latestStable || latestStable === null;
- }
-}
-
export default function(
state: VersionReduxState = initialState,
action: ReduxAction,
): VersionReduxState {
switch (action.type) {
case 'UPDATE_LATEST': {
- const currentIsSupported = action.latestInfo.currentIsSupported;
- const latest = action.latestInfo.latest.latest;
- const latestStable = action.latestInfo.latest.latestStable;
-
+ const { latest, ...other } = action.latestInfo;
return {
...state,
- currentIsSupported,
- latest,
- latestStable,
- nextUpgrade: nextUpgrade(state.current, latest, latestStable),
- upToDate: checkIfLatest(state.current, latest, latestStable),
+ ...other,
+ ...latest,
};
}
@@ -69,8 +41,6 @@ export default function(
...state,
current: action.version,
consistent: action.consistent,
- nextUpgrade: nextUpgrade(action.version, state.latest, state.latestStable),
- upToDate: checkIfLatest(action.version, state.latest, state.latestStable),
};
default: