summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
authorEmīls Piņķis <emils@mullvad.net>2019-10-07 14:29:24 +0100
committerEmīls Piņķis <emils@mullvad.net>2019-10-07 14:29:24 +0100
commit49014f53b3ea60a7c37930e2018314f2aac7d1eb (patch)
treed3438a609d90bf63a458804c5ebd90804e1477a2 /gui
parent4a82777ccb2de2bf31b7f464374fc0a5d76b9d4c (diff)
parent1b6abf7a9b59c23725f6fa544c87406f6a2684be (diff)
downloadmullvadvpn-49014f53b3ea60a7c37930e2018314f2aac7d1eb.tar.xz
mullvadvpn-49014f53b3ea60a7c37930e2018314f2aac7d1eb.zip
Merge branch 'gui-login-automatically'
Diffstat (limited to 'gui')
-rw-r--r--gui/src/config.json4
-rw-r--r--gui/src/main/daemon-rpc.ts9
-rw-r--r--gui/src/main/index.ts1
-rw-r--r--gui/src/renderer/app.tsx12
-rw-r--r--gui/src/renderer/components/Account.tsx17
-rw-r--r--gui/src/renderer/components/AppButton.tsx42
-rw-r--r--gui/src/renderer/components/Connect.tsx3
-rw-r--r--gui/src/renderer/components/NotificationArea.tsx4
-rw-r--r--gui/src/renderer/components/NotificationBanner.tsx28
-rw-r--r--gui/src/renderer/components/WireguardKeys.tsx16
-rw-r--r--gui/src/renderer/containers/AccountPage.tsx3
-rw-r--r--gui/src/renderer/containers/ConnectPage.tsx1
-rw-r--r--gui/src/renderer/containers/NotificationAreaContainer.tsx10
-rw-r--r--gui/src/renderer/containers/WireguardKeysPage.tsx3
-rw-r--r--gui/src/shared/ipc-event-channel.ts5
-rw-r--r--gui/test/components/NotificationArea.spec.tsx36
16 files changed, 139 insertions, 55 deletions
diff --git a/gui/src/config.json b/gui/src/config.json
index 95ed24c452..f3f0d99dca 100644
--- a/gui/src/config.json
+++ b/gui/src/config.json
@@ -1,8 +1,8 @@
{
"links": {
"createAccount": "https://mullvad.net/account/create/",
- "purchase": "https://mullvad.net/account/login/",
- "manageKeys": "https://mullvad.net/account/login/",
+ "purchase": "https://mullvad.net/account/",
+ "manageKeys": "https://mullvad.net/account/ports/",
"faq": "https://mullvad.net/faq/",
"download": "https://mullvad.net/download/",
"supportEmail": "support@mullvad.net"
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index 1ce4450af3..6105f75c56 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -428,6 +428,15 @@ export class DaemonRpc {
}
}
+ public async getWwwAuthToken(): Promise<string> {
+ const response = await this.transport.send('get_www_auth_token');
+ try {
+ return validate(string, response);
+ } catch (error) {
+ throw new ResponseParseError('Invalid response from get_www_auth_token', error);
+ }
+ }
+
public async getRelayLocations(): Promise<IRelayList> {
const response = await this.transport.send('get_relay_locations');
try {
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 5b5c58d108..9397d45467 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -991,6 +991,7 @@ class ApplicationMain {
IpcMainEventChannel.account.handleLogin((token: AccountToken) => this.login(token));
IpcMainEventChannel.account.handleLogout(() => this.logout());
+ IpcMainEventChannel.account.handleWwwAuthToken(() => this.daemonRpc.getWwwAuthToken());
IpcMainEventChannel.accountHistory.handleRemoveItem(async (token: AccountToken) => {
await this.daemonRpc.removeAccountFromHistory(token);
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index f9c2390b25..98ffbc3157 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -3,7 +3,7 @@ import {
push as pushHistory,
replace as replaceHistory,
} from 'connected-react-router';
-import { ipcRenderer, webFrame } from 'electron';
+import { ipcRenderer, shell, webFrame } from 'electron';
import log from 'electron-log';
import { createMemoryHistory } from 'history';
import * as React from 'react';
@@ -288,6 +288,16 @@ export default class AppRenderer {
return IpcRendererEventChannel.accountHistory.removeItem(accountToken);
}
+ public async openLinkWithAuth(link: string): Promise<void> {
+ let token = '';
+ try {
+ token = await IpcRendererEventChannel.account.getWwwAuthToken();
+ } catch (e) {
+ log.error(`Failed to get the WWW auth token: ${e.message}`);
+ }
+ shell.openExternal(`${link}?token=${token}`);
+ }
+
public async setAllowLan(allowLan: boolean) {
const actions = this.reduxActions;
await IpcRendererEventChannel.settings.setAllowLan(allowLan);
diff --git a/gui/src/renderer/components/Account.tsx b/gui/src/renderer/components/Account.tsx
index fb4eb6bf35..1751906026 100644
--- a/gui/src/renderer/components/Account.tsx
+++ b/gui/src/renderer/components/Account.tsx
@@ -18,7 +18,7 @@ interface IProps {
isOffline: boolean;
onLogout: () => void;
onClose: () => void;
- onBuyMore: () => void;
+ onBuyMore: () => Promise<void>;
}
export default class Account extends Component<IProps> {
@@ -65,15 +65,16 @@ export default class Account extends Component<IProps> {
</View>
<View style={styles.account__footer}>
- <AppButton.GreenButton
- style={styles.account__buy_button}
+ <AppButton.BlockingButton
disabled={this.props.isOffline}
onPress={this.props.onBuyMore}>
- <AppButton.Label>
- {messages.pgettext('account-view', 'Buy more credit')}
- </AppButton.Label>
- <AppButton.Icon source="icon-extLink" height={16} width={16} />
- </AppButton.GreenButton>
+ <AppButton.GreenButton style={styles.account__buy_button}>
+ <AppButton.Label>
+ {messages.pgettext('account-view', 'Buy more credit')}
+ </AppButton.Label>
+ <AppButton.Icon source="icon-extLink" height={16} width={16} />
+ </AppButton.GreenButton>
+ </AppButton.BlockingButton>
<AppButton.RedButton onPress={this.props.onLogout}>
{messages.pgettext('account-view', 'Log out')}
</AppButton.RedButton>
diff --git a/gui/src/renderer/components/AppButton.tsx b/gui/src/renderer/components/AppButton.tsx
index 34a7f46e04..6f14fd16e9 100644
--- a/gui/src/renderer/components/AppButton.tsx
+++ b/gui/src/renderer/components/AppButton.tsx
@@ -1,3 +1,4 @@
+import log from 'electron-log';
import * as React from 'react';
import { Button, Component, Styles, Text, Types, UserInterface, View } from 'reactxp';
import { colors } from '../../config.json';
@@ -162,6 +163,47 @@ class BaseButton extends Component<IProps, IState> {
};
}
+interface IBlockingState {
+ isBlocked: boolean;
+}
+
+interface IBlockingProps {
+ children?: React.ReactNode;
+ onPress: () => Promise<void>;
+ disabled?: boolean;
+}
+
+export class BlockingButton extends Component<IBlockingProps, IBlockingState> {
+ public state = {
+ isBlocked: false,
+ };
+
+ public render() {
+ return React.Children.map(this.props.children, (child) => {
+ if (React.isValidElement(child)) {
+ return React.cloneElement(child as React.ReactElement<any>, {
+ ...child.props,
+ disabled: this.state.isBlocked || this.props.disabled,
+ onPress: this.onPress,
+ });
+ } else {
+ return child;
+ }
+ });
+ }
+
+ private onPress = () => {
+ this.setState({ isBlocked: true }, async () => {
+ try {
+ await this.props.onPress();
+ } catch (error) {
+ log.error(`onPress() failed - ${error}`);
+ }
+ this.setState({ isBlocked: false });
+ });
+ };
+}
+
export class RedButton extends BaseButton {
protected backgroundStyle = () => (this.state.hovered ? styles.redHover : styles.red);
}
diff --git a/gui/src/renderer/components/Connect.tsx b/gui/src/renderer/components/Connect.tsx
index d3b9bd0f93..3763751c60 100644
--- a/gui/src/renderer/components/Connect.tsx
+++ b/gui/src/renderer/components/Connect.tsx
@@ -23,7 +23,8 @@ interface IProps {
onSelectLocation: () => void;
onConnect: () => void;
onDisconnect: () => void;
- onExternalLink: (url: string) => void;
+ onExternalLink: (url: string) => Promise<void>;
+ onExternalLinkWithAuth: (url: string) => Promise<void>;
}
type MarkerOrSpinner = 'marker' | 'spinner';
diff --git a/gui/src/renderer/components/NotificationArea.tsx b/gui/src/renderer/components/NotificationArea.tsx
index 01de4f2d83..1d65306ef9 100644
--- a/gui/src/renderer/components/NotificationArea.tsx
+++ b/gui/src/renderer/components/NotificationArea.tsx
@@ -24,8 +24,8 @@ interface IProps {
tunnelState: TunnelState;
version: IVersionReduxState;
blockWhenDisconnected: boolean;
- onOpenDownloadLink: () => void;
- onOpenBuyMoreLink: () => void;
+ onOpenDownloadLink: () => Promise<void>;
+ onOpenBuyMoreLink: () => Promise<void>;
}
type NotificationAreaPresentation =
diff --git a/gui/src/renderer/components/NotificationBanner.tsx b/gui/src/renderer/components/NotificationBanner.tsx
index 56efb41611..b8f935f3c0 100644
--- a/gui/src/renderer/components/NotificationBanner.tsx
+++ b/gui/src/renderer/components/NotificationBanner.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
import { Animated, Button, Component, Styles, Text, Types, UserInterface, View } from 'reactxp';
import { colors } from '../../config.json';
+import { BlockingButton } from './AppButton';
import ImageView from './ImageView';
const styles = {
@@ -83,25 +84,26 @@ export class NotificationSubtitle extends Component {
}
}
-export class NotificationOpenLinkAction extends Component<{ onPress: () => void }> {
+export class NotificationOpenLinkAction extends Component<{ onPress: () => Promise<void> }> {
public state = {
hovered: false,
};
public render() {
return (
- <Button
- style={styles.actionButton}
- onPress={this.props.onPress}
- onHoverStart={this.onHoverStart}
- onHoverEnd={this.onHoverEnd}>
- <ImageView
- height={12}
- width={12}
- tintColor={this.state.hovered ? colors.white80 : colors.white60}
- source="icon-extLink"
- />
- </Button>
+ <BlockingButton onPress={this.props.onPress}>
+ <Button
+ style={styles.actionButton}
+ onHoverStart={this.onHoverStart}
+ onHoverEnd={this.onHoverEnd}>
+ <ImageView
+ height={12}
+ width={12}
+ tintColor={this.state.hovered ? colors.white80 : colors.white60}
+ source="icon-extLink"
+ />
+ </Button>
+ </BlockingButton>
);
}
diff --git a/gui/src/renderer/components/WireguardKeys.tsx b/gui/src/renderer/components/WireguardKeys.tsx
index 966d751b4f..9a65fe92e9 100644
--- a/gui/src/renderer/components/WireguardKeys.tsx
+++ b/gui/src/renderer/components/WireguardKeys.tsx
@@ -21,7 +21,7 @@ export interface IProps {
onGenerateKey: () => void;
onReplaceKey: (old: IWgKey) => void;
onVerifyKey: (publicKey: IWgKey) => void;
- onVisitWebsiteKey: () => void;
+ onVisitWebsiteKey: () => Promise<void>;
}
export default class WireguardKeys extends Component<IProps> {
@@ -85,14 +85,16 @@ export default class WireguardKeys extends Component<IProps> {
</AppButton.GreenButton>
</View>
<View style={styles.wgkeys__row}>
- <AppButton.GreenButton
+ <AppButton.BlockingButton
disabled={this.props.isOffline}
onPress={this.props.onVisitWebsiteKey}>
- <AppButton.Label>
- {messages.pgettext('wireguard-key-view', 'Manage keys')}
- </AppButton.Label>
- <AppButton.Icon source="icon-extLink" height={16} width={16} />
- </AppButton.GreenButton>
+ <AppButton.GreenButton>
+ <AppButton.Label>
+ {messages.pgettext('wireguard-key-view', 'Manage keys')}
+ </AppButton.Label>
+ <AppButton.Icon source="icon-extLink" height={16} width={16} />
+ </AppButton.GreenButton>
+ </AppButton.BlockingButton>
</View>
</View>
</View>
diff --git a/gui/src/renderer/containers/AccountPage.tsx b/gui/src/renderer/containers/AccountPage.tsx
index 4880cec68e..0fea11da8b 100644
--- a/gui/src/renderer/containers/AccountPage.tsx
+++ b/gui/src/renderer/containers/AccountPage.tsx
@@ -1,5 +1,4 @@
import { goBack } from 'connected-react-router';
-import { shell } from 'electron';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { links } from '../../config.json';
@@ -23,7 +22,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => {
onClose: () => {
history.goBack();
},
- onBuyMore: () => shell.openExternal(links.purchase),
+ onBuyMore: () => props.app.openLinkWithAuth(links.purchase),
};
};
diff --git a/gui/src/renderer/containers/ConnectPage.tsx b/gui/src/renderer/containers/ConnectPage.tsx
index 8d30bdc149..b473a523c8 100644
--- a/gui/src/renderer/containers/ConnectPage.tsx
+++ b/gui/src/renderer/containers/ConnectPage.tsx
@@ -101,6 +101,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => {
}
},
onExternalLink: (url: string) => shell.openExternal(url),
+ onExternalLinkWithAuth: (url: string) => props.app.openLinkWithAuth(url),
};
};
diff --git a/gui/src/renderer/containers/NotificationAreaContainer.tsx b/gui/src/renderer/containers/NotificationAreaContainer.tsx
index 90c8938b9d..814b055d97 100644
--- a/gui/src/renderer/containers/NotificationAreaContainer.tsx
+++ b/gui/src/renderer/containers/NotificationAreaContainer.tsx
@@ -16,13 +16,13 @@ const mapStateToProps = (state: IReduxState, _props: IAppContext) => ({
blockWhenDisconnected: state.settings.blockWhenDisconnected,
});
-const mapDispatchToProps = (_dispatch: ReduxDispatch, _props: IAppContext) => {
+const mapDispatchToProps = (_dispatch: ReduxDispatch, props: IAppContext) => {
return {
- onOpenDownloadLink() {
- shell.openExternal(links.download);
+ onOpenDownloadLink(): Promise<void> {
+ return shell.openExternal(links.download);
},
- onOpenBuyMoreLink() {
- shell.openExternal(links.purchase);
+ onOpenBuyMoreLink(): Promise<void> {
+ return props.app.openLinkWithAuth(links.purchase);
},
};
};
diff --git a/gui/src/renderer/containers/WireguardKeysPage.tsx b/gui/src/renderer/containers/WireguardKeysPage.tsx
index 911093f9ca..9f5a4aa8d2 100644
--- a/gui/src/renderer/containers/WireguardKeysPage.tsx
+++ b/gui/src/renderer/containers/WireguardKeysPage.tsx
@@ -1,5 +1,4 @@
import { goBack, push } from 'connected-react-router';
-import { shell } from 'electron';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { links } from '../../config.json';
@@ -20,7 +19,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => {
onGenerateKey: () => props.app.generateWireguardKey(),
onReplaceKey: (oldKey: IWgKey) => props.app.replaceWireguardKey(oldKey),
onVerifyKey: (publicKey: IWgKey) => props.app.verifyWireguardKey(publicKey),
- onVisitWebsiteKey: () => shell.openExternal(links.manageKeys),
+ onVisitWebsiteKey: () => props.app.openLinkWithAuth(links.manageKeys),
};
};
diff --git a/gui/src/shared/ipc-event-channel.ts b/gui/src/shared/ipc-event-channel.ts
index 8a1a31c2d4..a612660c99 100644
--- a/gui/src/shared/ipc-event-channel.ts
+++ b/gui/src/shared/ipc-event-channel.ts
@@ -102,11 +102,13 @@ interface IGuiSettingsHandlers extends ISender<IGuiSettingsState> {
interface IAccountHandlers extends ISender<IAccountData | undefined> {
handleLogin(fn: (token: AccountToken) => Promise<void>): void;
handleLogout(fn: () => Promise<void>): void;
+ handleWwwAuthToken(fn: () => Promise<string>): void;
}
interface IAccountMethods extends IReceiver<IAccountData | undefined> {
login(token: AccountToken): Promise<void>;
logout(): Promise<void>;
+ getWwwAuthToken(): Promise<string>;
}
interface IAccountHistoryHandlers extends ISender<AccountToken[]> {
@@ -177,6 +179,7 @@ const REMOVE_ACCOUNT_HISTORY_ITEM = 'remove-account-history-item';
const DO_LOGIN = 'do-login';
const DO_LOGOUT = 'do-logout';
+const DO_GET_WWW_AUTH_TOKEN = 'do-get-www-auth-token';
const ACCOUNT_DATA_CHANGED = 'account-data-changed';
const AUTO_START_CHANGED = 'auto-start-changed';
@@ -267,6 +270,7 @@ export class IpcRendererEventChannel {
listen: listen(ACCOUNT_DATA_CHANGED),
login: requestSender(DO_LOGIN),
logout: requestSender(DO_LOGOUT),
+ getWwwAuthToken: requestSender(DO_GET_WWW_AUTH_TOKEN),
};
public static accountHistory: IAccountHistoryMethods = {
@@ -358,6 +362,7 @@ export class IpcMainEventChannel {
notify: sender<IAccountData | undefined>(ACCOUNT_DATA_CHANGED),
handleLogin: requestHandler(DO_LOGIN),
handleLogout: requestHandler(DO_LOGOUT),
+ handleWwwAuthToken: requestHandler(DO_GET_WWW_AUTH_TOKEN),
};
public static accountHistory: IAccountHistoryHandlers = {
diff --git a/gui/test/components/NotificationArea.spec.tsx b/gui/test/components/NotificationArea.spec.tsx
index 6b86639a75..713c30faff 100644
--- a/gui/test/components/NotificationArea.spec.tsx
+++ b/gui/test/components/NotificationArea.spec.tsx
@@ -33,7 +33,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -50,7 +51,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -78,7 +80,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -94,7 +97,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -110,7 +114,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={true}
/>,
);
@@ -126,7 +131,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -147,7 +153,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -167,7 +174,8 @@ describe('components/NotificationArea', () => {
consistent: false,
}}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -190,7 +198,8 @@ describe('components/NotificationArea', () => {
nextUpgrade: '2018.2',
}}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -214,7 +223,8 @@ describe('components/NotificationArea', () => {
nextUpgrade: '2018.3',
}}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -239,7 +249,8 @@ describe('components/NotificationArea', () => {
nextUpgrade: '2018.4-beta3',
}}
accountExpiry={defaultExpiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);
@@ -263,7 +274,8 @@ describe('components/NotificationArea', () => {
}}
version={defaultVersion}
accountExpiry={expiry}
- openExternalLink={() => {}}
+ onOpenDownloadLink={async () => {}}
+ onOpenBuyMoreLink={async () => {}}
blockWhenDisconnected={false}
/>,
);