summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-05-12 14:21:15 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-05-13 10:49:25 +0200
commit5c99fdee273b54a26224c79ef839fc3d9f615707 (patch)
tree71d4c36555ed0de0184ec945a39f81f4c315ad2f /gui/src
parent64f206b034c9480ba21fe89472724121bac321e0 (diff)
downloadmullvadvpn-5c99fdee273b54a26224c79ef839fc3d9f615707.tar.xz
mullvadvpn-5c99fdee273b54a26224c79ef839fc3d9f615707.zip
Update to new device state rpc calls
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/main/daemon-rpc.ts64
-rw-r--r--gui/src/main/index.ts102
-rw-r--r--gui/src/main/tray-icon-controller.ts28
-rw-r--r--gui/src/renderer/app.tsx112
-rw-r--r--gui/src/renderer/components/Account.tsx14
-rw-r--r--gui/src/renderer/containers/AccountPage.tsx2
-rw-r--r--gui/src/renderer/redux/account/actions.ts18
-rw-r--r--gui/src/shared/connect-helper.ts14
-rw-r--r--gui/src/shared/daemon-rpc-types.ts18
-rw-r--r--gui/src/shared/ipc-schema.ts11
10 files changed, 194 insertions, 189 deletions
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index 84e832cd5f..c40e40b864 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -15,14 +15,14 @@ import {
ConnectionConfig,
Constraint,
DaemonEvent,
+ DeviceEvent,
+ DeviceState,
ErrorStateCause,
FirewallPolicyError,
IAccountData,
IAppVersionInfo,
IBridgeConstraints,
IDevice,
- IDeviceConfig,
- IDeviceEvent,
IDeviceRemoval,
IDnsOptions,
IErrorState,
@@ -41,6 +41,8 @@ import {
ITunnelStateRelayInfo,
IWireguardConstraints,
IWireguardTunnelData,
+ LoggedInDeviceState,
+ LoggedOutDeviceState,
ObfuscationType,
ProxySettings,
ProxyType,
@@ -513,18 +515,9 @@ export class DaemonRpc {
return response.getValue();
}
- public async getDevice(): Promise<IDeviceConfig | undefined> {
- try {
- const response = await this.callEmpty<grpcTypes.DeviceConfig>(this.client.getDevice);
- return convertFromDeviceConfig(response);
- } catch (e) {
- const error = e as grpc.ServiceError;
- if (error.code === grpc.status.NOT_FOUND) {
- return undefined;
- } else {
- throw error;
- }
- }
+ public async getDevice(): Promise<DeviceState> {
+ const response = await this.callEmpty<grpcTypes.DeviceState>(this.client.getDevice);
+ return convertFromDeviceState(response);
}
public async updateDevice(): Promise<void> {
@@ -1409,21 +1402,40 @@ function convertToTransportProtocol(protocol: RelayProtocol): grpcTypes.Transpor
}
}
-function convertFromDeviceEvent(deviceEvent: grpcTypes.DeviceEvent): IDeviceEvent {
- return {
- deviceConfig: convertFromDeviceConfig(deviceEvent.getDevice()),
- remote: deviceEvent.getRemote(),
- };
+function convertFromDeviceEvent(deviceEvent: grpcTypes.DeviceEvent): DeviceEvent {
+ const deviceState = convertFromDeviceState(deviceEvent.getNewState()!);
+ switch (deviceEvent.getCause()) {
+ case grpcTypes.DeviceEvent.Cause.LOGGED_IN:
+ return { type: 'logged in', deviceState: deviceState as LoggedInDeviceState };
+ case grpcTypes.DeviceEvent.Cause.LOGGED_OUT:
+ return { type: 'logged out', deviceState: deviceState as LoggedOutDeviceState };
+ case grpcTypes.DeviceEvent.Cause.REVOKED:
+ return { type: 'revoked', deviceState: deviceState as LoggedOutDeviceState };
+ case grpcTypes.DeviceEvent.Cause.UPDATED:
+ return { type: 'updated', deviceState: deviceState as LoggedInDeviceState };
+ case grpcTypes.DeviceEvent.Cause.ROTATED_KEY:
+ return { type: 'rotated_key', deviceState: deviceState as LoggedInDeviceState };
+ }
}
-function convertFromDeviceConfig(deviceConfig?: grpcTypes.DeviceConfig): IDeviceConfig | undefined {
- const device = deviceConfig?.getDevice();
- return (
- deviceConfig && {
- accountToken: deviceConfig.getAccountToken(),
- device: device ? convertFromDevice(device) : undefined,
+function convertFromDeviceState(deviceState: grpcTypes.DeviceState): DeviceState {
+ switch (deviceState.getState()) {
+ case grpcTypes.DeviceState.State.LOGGED_IN: {
+ const accountAndDevice = deviceState.getDevice()!;
+ const device = accountAndDevice.getDevice();
+ return {
+ type: 'logged in',
+ accountAndDevice: {
+ accountToken: accountAndDevice.getAccountToken(),
+ device: device && convertFromDevice(device),
+ },
+ };
}
- );
+ case grpcTypes.DeviceState.State.LOGGED_OUT:
+ return { type: 'logged out' };
+ case grpcTypes.DeviceState.State.REVOKED:
+ return { type: 'revoked' };
+ }
}
function convertFromDeviceRemoval(deviceRemoval: grpcTypes.RemoveDeviceEvent): Array<IDevice> {
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index c1fb5407f7..4d0f915bb9 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -27,10 +27,10 @@ import {
BridgeSettings,
BridgeState,
DaemonEvent,
+ DeviceEvent,
+ DeviceState,
IAccountData,
IAppVersionInfo,
- IDeviceConfig,
- IDeviceEvent,
IDeviceRemoval,
IDnsOptions,
IRelayList,
@@ -206,8 +206,7 @@ class ApplicationMain {
},
},
};
- private deviceConfig?: IDeviceConfig;
- private hasReceivedDeviceConfig = false;
+ private deviceState?: DeviceState;
private guiSettings = new GuiSettings();
private tunnelStateExpectation?: Expectation;
@@ -681,10 +680,13 @@ class ApplicationMain {
// fetch device
try {
- this.setDeviceConfig({ deviceConfig: await this.daemonRpc.getDevice() });
- void this.daemonRpc
- .updateDevice()
- .catch((error: Error) => log.warn(`Failed to update device info: ${error.message}`));
+ const deviceState = await this.daemonRpc.getDevice();
+ this.handleDeviceEvent({ type: deviceState.type, deviceState } as DeviceEvent);
+ if (deviceState.type === 'logged in') {
+ void this.daemonRpc
+ .updateDevice()
+ .catch((error: Error) => log.warn(`Failed to update device info: ${error.message}`));
+ }
} catch (e) {
const error = e as Error;
log.error(`Failed to fetch device: ${error.message}`);
@@ -749,7 +751,7 @@ class ApplicationMain {
}
// show window when account is not set
- if (!this.deviceConfig) {
+ if (!this.isLoggedIn()) {
this.windowController?.show();
}
};
@@ -822,7 +824,7 @@ class ApplicationMain {
} else if ('appVersionInfo' in daemonEvent) {
this.setLatestVersion(daemonEvent.appVersionInfo);
} else if ('device' in daemonEvent) {
- this.setDeviceConfig(daemonEvent.device);
+ this.handleDeviceEvent(daemonEvent.device);
} else if ('deviceRemoval' in daemonEvent) {
if (this.windowController) {
IpcMainEventChannel.account.notifyDevices(
@@ -854,26 +856,14 @@ class ApplicationMain {
}
private connectTunnel = async (): Promise<void> => {
- if (
- connectEnabled(
- this.connectedToDaemon,
- this.deviceConfig?.accountToken,
- this.tunnelState.state,
- )
- ) {
+ if (connectEnabled(this.connectedToDaemon, this.isLoggedIn(), this.tunnelState.state)) {
this.setOptimisticTunnelState('connecting');
await this.daemonRpc.connectTunnel();
}
};
private reconnectTunnel = async (): Promise<void> => {
- if (
- reconnectEnabled(
- this.connectedToDaemon,
- this.deviceConfig?.accountToken,
- this.tunnelState.state,
- )
- ) {
+ if (reconnectEnabled(this.connectedToDaemon, this.isLoggedIn(), this.tunnelState.state)) {
this.setOptimisticTunnelState('connecting');
await this.daemonRpc.reconnectTunnel();
}
@@ -1166,28 +1156,41 @@ class ApplicationMain {
}
}
- private setDeviceConfig(deviceEvent: IDeviceEvent) {
- const oldDeviceConfig = this.deviceConfig;
- this.deviceConfig = deviceEvent.deviceConfig;
- this.hasReceivedDeviceConfig = true;
+ private handleDeviceEvent(deviceEvent: DeviceEvent) {
+ this.deviceState = deviceEvent.deviceState;
if (this.isPerformingPostUpgrade) {
void this.performPostUpgradeCheck();
}
- // make sure to invalidate the account data cache when account tokens change
- this.updateAccountDataOnAccountChange(
- oldDeviceConfig?.accountToken,
- deviceEvent.deviceConfig?.accountToken,
- );
+ switch (deviceEvent.deviceState.type) {
+ case 'logged in':
+ this.accountDataCache.fetch(deviceEvent.deviceState.accountAndDevice.accountToken);
+ break;
+ case 'logged out':
+ case 'revoked':
+ this.accountDataCache.invalidate();
+ break;
+ }
void this.updateAccountHistory();
+ this.setTrayContextMenu();
if (this.windowController) {
IpcMainEventChannel.account.notifyDevice(this.windowController.webContents, deviceEvent);
}
}
+ private isLoggedIn(): boolean {
+ return this.deviceState?.type === 'logged in';
+ }
+
+ private getAccountToken(): AccountToken | undefined {
+ return this.deviceState?.type === 'logged in'
+ ? this.deviceState.accountAndDevice.accountToken
+ : undefined;
+ }
+
private trayIconType(tunnelState: TunnelState, blockWhenDisconnected: boolean): TrayIconType {
switch (tunnelState.state) {
case 'connected':
@@ -1270,8 +1273,7 @@ class ApplicationMain {
tunnelState: this.tunnelState,
settings: this.settings,
isPerformingPostUpgrade: this.isPerformingPostUpgrade,
- deviceConfig: this.deviceConfig,
- hasReceivedDeviceConfig: this.hasReceivedDeviceConfig,
+ deviceState: this.deviceState,
relayListPair: {
relays: this.processRelaysForPresentation(this.relays, this.settings.relaySettings),
bridges: this.processBridgesForPresentation(this.relays, this.settings.bridgeState),
@@ -1363,7 +1365,7 @@ class ApplicationMain {
IpcMainEventChannel.account.handleLogout(() => this.logout());
IpcMainEventChannel.account.handleGetWwwAuthToken(() => this.daemonRpc.getWwwAuthToken());
IpcMainEventChannel.account.handleSubmitVoucher(async (voucherCode: string) => {
- const currentAccountToken = this.deviceConfig?.accountToken;
+ const currentAccountToken = this.getAccountToken();
const response = await this.daemonRpc.submitVoucher(voucherCode);
if (currentAccountToken) {
@@ -1374,9 +1376,9 @@ class ApplicationMain {
});
IpcMainEventChannel.account.handleUpdateData(() => this.updateAccountData());
- IpcMainEventChannel.account.handleGetDevice(async () => {
- const deviceConfig = await this.daemonRpc.getDevice();
- return deviceConfig?.device;
+ IpcMainEventChannel.account.handleGetDeviceState(async () => {
+ await this.daemonRpc.updateDevice();
+ return this.daemonRpc.getDevice();
});
IpcMainEventChannel.account.handleListDevices((accountToken: AccountToken) => {
return this.daemonRpc.listDevices(accountToken);
@@ -1540,7 +1542,7 @@ class ApplicationMain {
private async autoConnect() {
if (process.env.NODE_ENV === 'development') {
log.info('Skip autoconnect in development');
- } else if (this.deviceConfig && (!this.accountData || !hasExpired(this.accountData.expiry))) {
+ } else if (this.isLoggedIn() && (!this.accountData || !hasExpired(this.accountData.expiry))) {
if (this.guiSettings.autoConnect) {
try {
log.info('Autoconnect the tunnel');
@@ -1571,21 +1573,9 @@ class ApplicationMain {
}
}
- private updateAccountDataOnAccountChange(oldAccount?: string, newAccount?: string) {
- if (oldAccount && !newAccount) {
- this.accountDataCache.invalidate();
- } else if (!oldAccount && newAccount) {
- this.accountDataCache.fetch(newAccount);
- } else if (oldAccount && newAccount && oldAccount !== newAccount) {
- this.accountDataCache.fetch(newAccount);
- }
-
- this.setTrayContextMenu();
- }
-
private updateAccountData() {
- if (this.connectedToDaemon && this.deviceConfig) {
- this.accountDataCache.fetch(this.deviceConfig.accountToken);
+ if (this.connectedToDaemon && this.isLoggedIn()) {
+ this.accountDataCache.fetch(this.getAccountToken()!);
}
}
@@ -1971,7 +1961,7 @@ class ApplicationMain {
this.tray?.on('right-click', () =>
this.trayIconController?.popUpContextMenu(
this.connectedToDaemon,
- this.deviceConfig?.accountToken,
+ this.isLoggedIn(),
this.tunnelState,
),
);
@@ -2009,7 +1999,7 @@ class ApplicationMain {
private setTrayContextMenu() {
this.trayIconController?.setContextMenu(
this.connectedToDaemon,
- this.deviceConfig?.accountToken,
+ this.isLoggedIn(),
this.tunnelState,
);
}
diff --git a/gui/src/main/tray-icon-controller.ts b/gui/src/main/tray-icon-controller.ts
index 81af2e174e..723228eac6 100644
--- a/gui/src/main/tray-icon-controller.ts
+++ b/gui/src/main/tray-icon-controller.ts
@@ -5,7 +5,7 @@ import { sprintf } from 'sprintf-js';
import { promisify } from 'util';
import { connectEnabled, disconnectEnabled, reconnectEnabled } from '../shared/connect-helper';
-import { AccountToken, ILocation, TunnelState } from '../shared/daemon-rpc-types';
+import { ILocation, TunnelState } from '../shared/daemon-rpc-types';
import { messages, relayLocations } from '../shared/gettext';
import log from '../shared/logging';
import KeyframeAnimation from './keyframe-animation';
@@ -102,15 +102,9 @@ export default class TrayIconController {
animation.play({ end: frame });
}
- public setContextMenu(
- connectedToDaemon: boolean,
- accountToken: AccountToken | undefined,
- tunnelState: TunnelState,
- ) {
+ public setContextMenu(connectedToDaemon: boolean, loggedIn: boolean, tunnelState: TunnelState) {
if (process.platform === 'linux') {
- this.tray.setContextMenu(
- this.createContextMenu(connectedToDaemon, accountToken, tunnelState),
- );
+ this.tray.setContextMenu(this.createContextMenu(connectedToDaemon, loggedIn, tunnelState));
}
}
@@ -119,14 +113,8 @@ export default class TrayIconController {
this.tray?.setToolTip(tooltip);
}
- public popUpContextMenu(
- connectedToDaemon: boolean,
- accountToken: AccountToken | undefined,
- tunnelState: TunnelState,
- ) {
- this.tray.popUpContextMenu(
- this.createContextMenu(connectedToDaemon, accountToken, tunnelState),
- );
+ public popUpContextMenu(connectedToDaemon: boolean, loggedIn: boolean, tunnelState: TunnelState) {
+ this.tray.popUpContextMenu(this.createContextMenu(connectedToDaemon, loggedIn, tunnelState));
}
private createTooltipText(connectedToDaemon: boolean, tunnelState: TunnelState): string {
@@ -267,7 +255,7 @@ export default class TrayIconController {
private createContextMenu(
connectedToDaemon: boolean,
- accountToken: AccountToken | undefined,
+ loggedIn: boolean,
tunnelState: TunnelState,
) {
const template: Electron.MenuItemConstructorOptions[] = [
@@ -281,13 +269,13 @@ export default class TrayIconController {
{
id: 'connect',
label: messages.gettext('Connect'),
- enabled: connectEnabled(connectedToDaemon, accountToken, tunnelState.state),
+ enabled: connectEnabled(connectedToDaemon, loggedIn, tunnelState.state),
click: this.connect,
},
{
id: 'reconnect',
label: messages.gettext('Reconnect'),
- enabled: reconnectEnabled(connectedToDaemon, accountToken, tunnelState.state),
+ enabled: reconnectEnabled(connectedToDaemon, loggedIn, tunnelState.state),
click: this.reconnect,
},
{
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index d6911da317..886a83d8d7 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -7,11 +7,11 @@ import {
AccountToken,
BridgeSettings,
BridgeState,
+ DeviceEvent,
+ DeviceState,
IAccountData,
IAppVersionInfo,
IDevice,
- IDeviceConfig,
- IDeviceEvent,
IDeviceRemoval,
IDnsOptions,
ILocation,
@@ -100,8 +100,7 @@ export default class AppRenderer {
private relayListPair!: IRelayListPair;
private tunnelState!: TunnelState;
private settings!: ISettings;
- private deviceConfig?: IDeviceConfig;
- private hasReceivedDeviceConfig = false;
+ private deviceState?: DeviceState;
private guiSettings!: IGuiSettingsState;
private loginState: LoginState = 'none';
private previousLoginState: LoginState = 'none';
@@ -136,9 +135,7 @@ export default class AppRenderer {
});
IpcRendererEventChannel.account.listenDevice((deviceEvent) => {
- const oldDeviceConfig = this.deviceConfig;
- this.hasReceivedDeviceConfig = true;
- this.handleAccountChange(deviceEvent, oldDeviceConfig?.accountToken);
+ this.handleDeviceEvent(deviceEvent);
});
IpcRendererEventChannel.account.listenDevices((devices) => {
@@ -213,12 +210,15 @@ export default class AppRenderer {
this.setAccountExpiry(initialState.accountData?.expiry);
this.setSettings(initialState.settings);
this.setIsPerformingPostUpgrade(initialState.isPerformingPostUpgrade);
- this.handleAccountChange(
- { deviceConfig: initialState.deviceConfig },
- undefined,
- initialState.navigationHistory !== undefined,
- );
- this.hasReceivedDeviceConfig = initialState.hasReceivedDeviceConfig;
+
+ if (initialState.deviceState) {
+ const deviceState = initialState.deviceState;
+ this.handleDeviceEvent(
+ { type: deviceState.type, deviceState } as DeviceEvent,
+ initialState.navigationHistory !== undefined,
+ );
+ }
+
this.setAccountHistory(initialState.accountHistory);
this.setTunnelState(initialState.tunnelState);
this.updateBlockedState(initialState.tunnelState, initialState.settings.blockWhenDisconnected);
@@ -363,8 +363,8 @@ export default class AppRenderer {
IpcRendererEventChannel.account.updateData();
}
- public getDevice = (): Promise<IDevice | undefined> => {
- return IpcRendererEventChannel.account.getDevice();
+ public getDeviceState = (): Promise<DeviceState> => {
+ return IpcRendererEventChannel.account.getDeviceState();
};
public fetchDevices = async (accountToken: AccountToken): Promise<Array<IDevice>> => {
@@ -589,6 +589,10 @@ export default class AppRenderer {
IpcRendererEventChannel.navigation.setScrollPositions(scrollPositions);
}
+ private isLoggedIn(): boolean {
+ return this.deviceState?.type === 'logged in';
+ }
+
// Make sure that the content height is correct and log if it isn't. This is mostly for debugging
// purposes since there's a bug in Electron that causes the app height to be another value than
// the one we have set.
@@ -735,13 +739,13 @@ export default class AppRenderer {
}
private getNavigationBase(): RoutePath {
- if (this.connectedToDaemon && this.hasReceivedDeviceConfig) {
+ if (this.connectedToDaemon && this.deviceState !== undefined) {
const loginState = this.reduxStore.getState().account.status;
const deviceRevoked = loginState.type === 'none' && loginState.deviceRevoked;
if (deviceRevoked) {
return RoutePath.deviceRevoked;
- } else if (this.deviceConfig?.accountToken) {
+ } else if (this.isLoggedIn()) {
return RoutePath.main;
} else {
return RoutePath.login;
@@ -838,48 +842,54 @@ export default class AppRenderer {
}
}
- private handleAccountChange(
- newDeviceEvent: IDeviceEvent,
- oldAccount?: string,
- preventRedirectToConnect?: boolean,
- ) {
+ private handleDeviceEvent(deviceEvent: DeviceEvent, preventRedirectToConnect?: boolean) {
const reduxAccount = this.reduxActions.account;
- this.deviceConfig = newDeviceEvent.deviceConfig;
- const newAccount = newDeviceEvent.deviceConfig?.accountToken;
- const newDevice = newDeviceEvent.deviceConfig?.device;
+ this.deviceState = deviceEvent.deviceState;
- if (oldAccount && !newAccount) {
- this.loginScheduler.cancel();
- if (!this.reduxStore.getState().account.loggingOut && newDeviceEvent.remote) {
- reduxAccount.deviceRevoked();
- } else {
- reduxAccount.loggedOut();
- }
+ switch (deviceEvent.type) {
+ case 'logged in': {
+ const accountToken = deviceEvent.deviceState.accountAndDevice.accountToken;
+ const device = deviceEvent.deviceState.accountAndDevice.device;
- this.resetNavigation();
- } else if (newAccount !== undefined && newDevice !== undefined && oldAccount !== newAccount) {
- switch (this.loginState) {
- case 'none':
- case 'logging in':
- reduxAccount.loggedIn({ accountToken: newAccount, device: newDevice });
+ switch (this.loginState) {
+ case 'none':
+ case 'logging in':
+ reduxAccount.loggedIn(accountToken, device);
- if (this.previousLoginState === 'too many devices') {
- this.resetNavigation();
- } else if (!preventRedirectToConnect) {
- this.redirectToConnect();
- }
- break;
- case 'creating account':
- reduxAccount.accountCreated(
- { accountToken: newAccount, device: newDevice },
- new Date().toISOString(),
- );
- break;
+ if (this.previousLoginState === 'too many devices') {
+ this.resetNavigation();
+ } else if (!preventRedirectToConnect) {
+ this.redirectToConnect();
+ }
+ break;
+ case 'creating account':
+ reduxAccount.accountCreated(accountToken, device, new Date().toISOString());
+ break;
+ }
+
+ if (this.loginState !== 'logging in' && this.loginState !== 'creating account') {
+ this.resetNavigation();
+ }
+ break;
}
+ case 'logged out':
+ this.loginScheduler.cancel();
+ reduxAccount.loggedOut();
+ this.resetNavigation();
+ break;
+ case 'revoked': {
+ this.loginScheduler.cancel();
+
+ const oldAccountState = this.reduxStore.getState().account;
+ if (oldAccountState.loggingOut) {
+ reduxAccount.loggedOut();
+ } else {
+ reduxAccount.deviceRevoked();
+ }
- if (this.loginState !== 'logging in' && this.loginState !== 'creating account') {
this.resetNavigation();
+ break;
}
}
diff --git a/gui/src/renderer/components/Account.tsx b/gui/src/renderer/components/Account.tsx
index af714273af..e0596a2b79 100644
--- a/gui/src/renderer/components/Account.tsx
+++ b/gui/src/renderer/components/Account.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { formatDate, hasExpired } from '../../shared/account-expiry';
-import { AccountToken, IDevice } from '../../shared/daemon-rpc-types';
+import { AccountToken, DeviceState } from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import {
AccountContainer,
@@ -39,7 +39,7 @@ interface IProps {
onClose: () => void;
onBuyMore: () => Promise<void>;
updateAccountData: () => void;
- getDevice: () => Promise<IDevice | undefined>;
+ getDeviceState: () => Promise<DeviceState | undefined>;
}
interface IState {
@@ -189,10 +189,12 @@ export default class Account extends React.Component<IProps, IState> {
this.setState({ logoutDialogState: 'checking-ports' });
this.props.prepareLogout();
- const device = await this.props.getDevice();
- if (device === undefined) {
- this.onHideLogoutConfirmationDialog();
- } else if (device.ports !== undefined && device.ports.length > 0) {
+ const deviceState = await this.props.getDeviceState();
+ if (
+ deviceState?.type === 'logged in' &&
+ deviceState.accountAndDevice.device?.ports !== undefined &&
+ deviceState.accountAndDevice.device.ports.length > 0
+ ) {
this.setState({ logoutDialogState: 'confirm' });
} else {
this.props.onLogout();
diff --git a/gui/src/renderer/containers/AccountPage.tsx b/gui/src/renderer/containers/AccountPage.tsx
index a813b12002..a81676c48b 100644
--- a/gui/src/renderer/containers/AccountPage.tsx
+++ b/gui/src/renderer/containers/AccountPage.tsx
@@ -29,7 +29,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IHistoryProps & IApp
},
onBuyMore: () => props.app.openLinkWithAuth(links.purchase),
updateAccountData: () => props.app.updateAccountData(),
- getDevice: () => props.app.getDevice(),
+ getDeviceState: () => props.app.getDeviceState(),
};
};
diff --git a/gui/src/renderer/redux/account/actions.ts b/gui/src/renderer/redux/account/actions.ts
index ba9d524cc0..cfa672a407 100644
--- a/gui/src/renderer/redux/account/actions.ts
+++ b/gui/src/renderer/redux/account/actions.ts
@@ -1,4 +1,4 @@
-import { AccountToken, IDevice, IDeviceConfig } from '../../../shared/daemon-rpc-types';
+import { AccountToken, IDevice } from '../../../shared/daemon-rpc-types';
interface IStartLoginAction {
type: 'START_LOGIN';
@@ -107,11 +107,11 @@ function startLogin(accountToken: AccountToken): IStartLoginAction {
};
}
-function loggedIn(deviceConfig: IDeviceConfig): ILoggedInAction {
+function loggedIn(accountToken: AccountToken, device?: IDevice): ILoggedInAction {
return {
type: 'LOGGED_IN',
- accountToken: deviceConfig.accountToken,
- deviceName: deviceConfig.device?.name,
+ accountToken,
+ deviceName: device?.name,
};
}
@@ -172,11 +172,15 @@ function createAccountFailed(error: Error): ICreateAccountFailed {
};
}
-function accountCreated(deviceConfig: IDeviceConfig, expiry: string): IAccountCreated {
+function accountCreated(
+ accountToken: AccountToken,
+ device: IDevice | undefined,
+ expiry: string,
+): IAccountCreated {
return {
type: 'ACCOUNT_CREATED',
- accountToken: deviceConfig.accountToken,
- deviceName: deviceConfig.device?.name,
+ accountToken: accountToken,
+ deviceName: device?.name,
expiry,
};
}
diff --git a/gui/src/shared/connect-helper.ts b/gui/src/shared/connect-helper.ts
index b1421cd8ef..d5c563c7ca 100644
--- a/gui/src/shared/connect-helper.ts
+++ b/gui/src/shared/connect-helper.ts
@@ -1,28 +1,24 @@
-import { AccountToken, TunnelState } from './daemon-rpc-types';
+import { TunnelState } from './daemon-rpc-types';
export function connectEnabled(
connectedToDaemon: boolean,
- accountToken: AccountToken | undefined,
+ loggedIn: boolean,
tunnelState: TunnelState['state'],
) {
return (
connectedToDaemon &&
- accountToken !== undefined &&
- accountToken !== '' &&
+ loggedIn &&
(tunnelState === 'disconnected' || tunnelState === 'disconnecting' || tunnelState === 'error')
);
}
export function reconnectEnabled(
connectedToDaemon: boolean,
- accountToken: AccountToken | undefined,
+ loggedIn: boolean,
tunnelState: TunnelState['state'],
) {
return (
- connectedToDaemon &&
- accountToken !== undefined &&
- accountToken !== '' &&
- (tunnelState === 'connected' || tunnelState === 'connecting')
+ connectedToDaemon && loggedIn && (tunnelState === 'connected' || tunnelState === 'connecting')
);
}
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index d1eab9cdce..aad35a01f4 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -114,7 +114,7 @@ export type DaemonEvent =
| { settings: ISettings }
| { relayList: IRelayList }
| { appVersionInfo: IAppVersionInfo }
- | { device: IDeviceEvent }
+ | { device: DeviceEvent }
| { deviceRemoval: Array<IDevice> };
export interface ITunnelStateRelayInfo {
@@ -333,16 +333,20 @@ export interface IAppVersionInfo {
suggestedIsBeta?: boolean;
}
-export interface IDeviceEvent {
- deviceConfig?: IDeviceConfig;
- remote?: boolean;
-}
-
-export interface IDeviceConfig {
+export interface IAccountAndDevice {
accountToken: AccountToken;
device?: IDevice;
}
+export type LoggedInDeviceState = { type: 'logged in'; accountAndDevice: IAccountAndDevice };
+export type LoggedOutDeviceState = { type: 'logged out' | 'revoked' };
+
+export type DeviceState = LoggedInDeviceState | LoggedOutDeviceState;
+
+export type DeviceEvent =
+ | { type: 'logged in' | 'updated' | 'rotated_key'; deviceState: LoggedInDeviceState }
+ | { type: 'logged out' | 'revoked'; deviceState: LoggedOutDeviceState };
+
export interface IDevice {
id: string;
name: string;
diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts
index e09d552c25..cbe29ee783 100644
--- a/gui/src/shared/ipc-schema.ts
+++ b/gui/src/shared/ipc-schema.ts
@@ -5,11 +5,11 @@ import {
AccountToken,
BridgeSettings,
BridgeState,
+ DeviceEvent,
+ DeviceState,
IAccountData,
IAppVersionInfo,
IDevice,
- IDeviceConfig,
- IDeviceEvent,
IDeviceRemoval,
IDnsOptions,
ILocation,
@@ -62,8 +62,7 @@ export interface IAppStateSnapshot {
tunnelState: TunnelState;
settings: ISettings;
isPerformingPostUpgrade: boolean;
- deviceConfig?: IDeviceConfig;
- hasReceivedDeviceConfig: boolean;
+ deviceState?: DeviceState;
relayListPair: IRelayListPair;
currentVersion: ICurrentAppVersionInfo;
upgradeVersion: IAppVersionInfo;
@@ -182,7 +181,7 @@ export const ipcSchema = {
},
account: {
'': notifyRenderer<IAccountData | undefined>(),
- device: notifyRenderer<IDeviceEvent>(),
+ device: notifyRenderer<DeviceEvent>(),
devices: notifyRenderer<Array<IDevice>>(),
create: invoke<void, string>(),
login: invoke<AccountToken, void>(),
@@ -190,7 +189,7 @@ export const ipcSchema = {
getWwwAuthToken: invoke<void, string>(),
submitVoucher: invoke<string, VoucherResponse>(),
updateData: send<void>(),
- getDevice: invoke<void, IDevice | undefined>(),
+ getDeviceState: invoke<void, DeviceState>(),
listDevices: invoke<AccountToken, Array<IDevice>>(),
removeDevice: invoke<IDeviceRemoval, void>(),
},