diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-09-06 17:28:57 +0300 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-09-10 17:49:14 +0300 |
| commit | 854e3ae2e4fdfefb0c56faa56807e5e163238542 (patch) | |
| tree | bfa292400304ac6ed63ce8c60aa4035c39abb9b4 | |
| parent | 91d3fe07688df60b435f934fce06e19a88560fb4 (diff) | |
| download | mullvadvpn-854e3ae2e4fdfefb0c56faa56807e5e163238542.tar.xz mullvadvpn-854e3ae2e4fdfefb0c56faa56807e5e163238542.zip | |
Add disconnecting state
4 files changed, 86 insertions, 60 deletions
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index 54275ec76a..04cbc9f6da 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -48,6 +48,7 @@ export default class AppRenderer { _reduxActions: *; _accountDataState = new AccountDataState(); _connectedToDaemon = false; + _tunnelState: ?TunnelState; constructor() { const store = configureStore(null, this._memoryHistory); @@ -229,18 +230,22 @@ export default class AppRenderer { async connectTunnel() { const actions = this._reduxActions; - try { - const currentState = await this._daemonRpc.getState(); - if (currentState === 'connected' || currentState === 'connecting') { - log.debug('Refusing to connect as connection is already secured'); - actions.connection.connected(); - } else { - actions.connection.connecting(); - await this._daemonRpc.connectTunnel(); - } - } catch (error) { - actions.connection.disconnected(); - throw error; + // avoid connecting when there is no account set in daemon. + const accountToken = await this._daemonRpc.getAccount(); + if (!accountToken) { + throw new NoAccountError(); + } + + // connect only if tunnel is disconnected or blocked. + if ( + this._tunnelState && + (this._tunnelState.state === 'disconnected' || this._tunnelState.state === 'blocked') + ) { + // switch to connecting state immediately to prevent a lag that may be caused by RPC + // communication delay + actions.connection.connecting(); + + await this._daemonRpc.connectTunnel(); } } @@ -415,8 +420,11 @@ export default class AppRenderer { } async _fetchTunnelState() { - const tunnelState = await this._daemonRpc.getState(); - this._updateConnectionState(tunnelState); + const state = await this._daemonRpc.getState(); + + log.debug(`Got state: ${JSON.stringify(state)}`); + + this._onChangeTunnelState(state); } async _fetchTunnelOptions() { @@ -511,14 +519,13 @@ export default class AppRenderer { async _subscribeStateListener() { await this._daemonRpc.subscribeStateListener((newState, error) => { if (error) { - log.error(`Received an error when processing the incoming state change: ${error.message}`); + log.error(`Failed to deserialize the new state: ${error.message}`); } if (newState) { - log.debug(`Got new state from daemon '${JSON.stringify(newState)}'`); + log.debug(`Got state update: '${JSON.stringify(newState)}'`); - this._updateConnectionState(newState); - this._refreshStateOnChange(); + this._onChangeTunnelState(newState); } }); } @@ -537,44 +544,28 @@ export default class AppRenderer { ]); } - _updateTrayIcon(connectionState: ConnectionState) { - const iconTypes: { [ConnectionState]: TrayIconType } = { - connected: 'secured', - connecting: 'securing', - blocked: 'securing', - }; - const type = iconTypes[connectionState] || 'unsecured'; - - ipcRenderer.send('change-tray-icon', type); - } + _onChangeTunnelState(tunnelState: TunnelState) { + this._tunnelState = tunnelState; - async _refreshStateOnChange() { - try { - await this._fetchLocation(); - } catch (error) { - log.error(`Failed to fetch the location: ${error.message}`); - } + this._updateConnectionStatus(tunnelState); + this._updateConnectionLocation(tunnelState.state); + this._updateTrayIcon(tunnelState.state); + this._showNotification(tunnelState.state); } - _tunnelStateToConnectionState(tunnelState: TunnelState): ConnectionState { - switch (tunnelState.state) { - case 'disconnected': - // Fall through - case 'disconnecting': - return 'disconnected'; - case 'connected': - return 'connected'; - case 'connecting': - return 'connecting'; - case 'blocked': - return 'blocked'; - default: - throw new Error('Unknown tunnel state: ' + (tunnelState: empty)); + async _updateConnectionLocation(connectionState: ConnectionState) { + if (connectionState === 'connecting' || connectionState === 'disconnected') { + try { + await this._fetchLocation(); + } catch (error) { + log.error(`Failed to update the location: ${error.message}`); + } } } - _updateConnectionState(tunnelState: TunnelState) { + _updateConnectionStatus(tunnelState: TunnelState) { const actions = this._reduxActions; + switch (tunnelState.state) { case 'connecting': actions.connection.connecting(); @@ -583,7 +574,8 @@ export default class AppRenderer { actions.connection.connected(); break; case 'disconnecting': - // Fall through + actions.connection.disconnecting(); + break; case 'disconnected': actions.connection.disconnected(); break; @@ -593,10 +585,17 @@ export default class AppRenderer { default: log.error(`Unexpected TunnelState: ${(tunnelState: empty)}`); } + } + + _updateTrayIcon(connectionState: ConnectionState) { + const iconTypes: { [ConnectionState]: TrayIconType } = { + connected: 'secured', + connecting: 'securing', + blocked: 'securing', + }; + const type = iconTypes[connectionState] || 'unsecured'; - const connectionState = this._tunnelStateToConnectionState(tunnelState); - this._updateTrayIcon(connectionState); - this._showNotification(connectionState); + ipcRenderer.send('change-tray-icon', type); } _showNotification(connectionState: ConnectionState) { @@ -613,6 +612,9 @@ export default class AppRenderer { case 'blocked': this._notificationController.show('Blocked all connections'); break; + case 'disconnecting': + // no-op + break; default: log.error(`Unexpected ConnectionState: ${(connectionState: empty)}`); return; diff --git a/gui/packages/desktop/src/renderer/components/Connect.js b/gui/packages/desktop/src/renderer/components/Connect.js index 078883a23b..f06ea86eb1 100644 --- a/gui/packages/desktop/src/renderer/components/Connect.js +++ b/gui/packages/desktop/src/renderer/components/Connect.js @@ -154,7 +154,7 @@ export default class Connect extends Component<Props, State> { } renderMap() { - let [isConnecting, isConnected, isDisconnected] = [false, false, false]; + let [isConnecting, isConnected, isDisconnected, isDisconnecting] = [false, false, false, false]; switch (this.props.connection.status) { case 'connecting': isConnecting = true; @@ -165,6 +165,9 @@ export default class Connect extends Component<Props, State> { case 'disconnected': isDisconnected = true; break; + case 'disconnecting': + isDisconnecting = true; + break; } return ( @@ -193,8 +196,8 @@ export default class Connect extends Component<Props, State> { ********************************** */} - {/* location when connecting or disconnected */} - {isConnecting || isDisconnected ? ( + {/* location when connecting, disconnecting or disconnected */} + {isConnecting || isDisconnecting || isDisconnected ? ( <Text style={styles.status_location} testName="location"> {this.props.connection.country} </Text> @@ -216,7 +219,7 @@ export default class Connect extends Component<Props, State> { */} <Text style={this.ipAddressStyle()} onPress={this.onIPAddressClick.bind(this)}> - {isConnected || isDisconnected ? ( + {isConnected || isDisconnecting || isDisconnected ? ( <Text testName="ipAddress"> {this.state.showCopyIPMessage ? 'IP copied to clipboard!' @@ -232,8 +235,8 @@ export default class Connect extends Component<Props, State> { ********************************** */} - {/* footer when disconnected */} - {isDisconnected ? ( + {/* footer when disconnecting or disconnected */} + {isDisconnecting || isDisconnected ? ( <View style={styles.footer}> <AppButton.TransparentButton style={styles.switch_location_button} @@ -324,6 +327,7 @@ export default class Connect extends Component<Props, State> { headerBarStyle(): HeaderBarStyle { const { status } = this.props.connection; switch (status) { + case 'disconnecting': case 'disconnected': return 'error'; case 'connecting': diff --git a/gui/packages/desktop/src/renderer/redux/connection/actions.js b/gui/packages/desktop/src/renderer/redux/connection/actions.js index 052d206986..fb920673e0 100644 --- a/gui/packages/desktop/src/renderer/redux/connection/actions.js +++ b/gui/packages/desktop/src/renderer/redux/connection/actions.js @@ -14,6 +14,10 @@ type DisconnectedAction = { type: 'DISCONNECTED', }; +type DisconnectingAction = { + type: 'DISCONNECTING', +}; + type BlockedAction = { type: 'BLOCKED', reason: string, @@ -44,6 +48,7 @@ export type ConnectionAction = | ConnectingAction | ConnectedAction | DisconnectedAction + | DisconnectingAction | BlockedAction | OnlineAction | OfflineAction; @@ -66,6 +71,12 @@ function disconnected(): DisconnectedAction { }; } +function disconnecting(): DisconnectingAction { + return { + type: 'DISCONNECTING', + }; +} + function blocked(reason: string): BlockedAction { return { type: 'BLOCKED', @@ -97,6 +108,7 @@ export default { connecting, connected, disconnected, + disconnecting, blocked, online, offline, diff --git a/gui/packages/desktop/src/renderer/redux/connection/reducers.js b/gui/packages/desktop/src/renderer/redux/connection/reducers.js index d425dec438..71f53b3a4c 100644 --- a/gui/packages/desktop/src/renderer/redux/connection/reducers.js +++ b/gui/packages/desktop/src/renderer/redux/connection/reducers.js @@ -3,7 +3,12 @@ import type { ReduxAction } from '../store'; import type { Ip } from '../../lib/daemon-rpc'; -export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'blocked'; +export type ConnectionState = + | 'disconnected' + | 'disconnecting' + | 'connecting' + | 'connected' + | 'blocked'; export type ConnectionReduxState = { status: ConnectionState, isOnline: boolean, @@ -43,6 +48,9 @@ export default function( case 'DISCONNECTED': return { ...state, ...{ status: 'disconnected', blockReason: null } }; + case 'DISCONNECTING': + return { ...state, ...{ status: 'disconnecting', blockReason: null } }; + case 'BLOCKED': return { ...state, ...{ status: 'blocked', blockReason: action.reason } }; |
