diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2019-10-07 14:29:24 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2019-10-07 14:29:24 +0100 |
| commit | 49014f53b3ea60a7c37930e2018314f2aac7d1eb (patch) | |
| tree | d3438a609d90bf63a458804c5ebd90804e1477a2 /gui | |
| parent | 4a82777ccb2de2bf31b7f464374fc0a5d76b9d4c (diff) | |
| parent | 1b6abf7a9b59c23725f6fa544c87406f6a2684be (diff) | |
| download | mullvadvpn-49014f53b3ea60a7c37930e2018314f2aac7d1eb.tar.xz mullvadvpn-49014f53b3ea60a7c37930e2018314f2aac7d1eb.zip | |
Merge branch 'gui-login-automatically'
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/src/config.json | 4 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 9 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 1 | ||||
| -rw-r--r-- | gui/src/renderer/app.tsx | 12 | ||||
| -rw-r--r-- | gui/src/renderer/components/Account.tsx | 17 | ||||
| -rw-r--r-- | gui/src/renderer/components/AppButton.tsx | 42 | ||||
| -rw-r--r-- | gui/src/renderer/components/Connect.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/NotificationArea.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/NotificationBanner.tsx | 28 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardKeys.tsx | 16 | ||||
| -rw-r--r-- | gui/src/renderer/containers/AccountPage.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ConnectPage.tsx | 1 | ||||
| -rw-r--r-- | gui/src/renderer/containers/NotificationAreaContainer.tsx | 10 | ||||
| -rw-r--r-- | gui/src/renderer/containers/WireguardKeysPage.tsx | 3 | ||||
| -rw-r--r-- | gui/src/shared/ipc-event-channel.ts | 5 | ||||
| -rw-r--r-- | gui/test/components/NotificationArea.spec.tsx | 36 |
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} />, ); |
