diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-03-02 17:50:24 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-04-06 10:37:05 +0200 |
| commit | 0f21406fed50f04913ad6c36096b579eac8f0282 (patch) | |
| tree | 0fc25b675fbcdbeb5e4c2b23e30a9e1847d6f518 /gui/src | |
| parent | 43294fbf8bead7f2bb28a84e8ff6fddc7100ec23 (diff) | |
| download | mullvadvpn-0f21406fed50f04913ad6c36096b579eac8f0282.tar.xz mullvadvpn-0f21406fed50f04913ad6c36096b579eac8f0282.zip | |
Merge NewAccountView.tsx into ExpiredAccountErrorView.tsx
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/AccountTokenLabel.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/ClipboardLabel.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/components/Connect.tsx | 64 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorView.tsx | 249 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx (renamed from gui/src/renderer/components/NewAccountViewStyles.tsx) | 35 | ||||
| -rw-r--r-- | gui/src/renderer/components/NewAccountView.tsx | 68 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ConnectPage.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx (renamed from gui/src/renderer/containers/NewAccountViewContainer.tsx) | 18 |
8 files changed, 172 insertions, 274 deletions
diff --git a/gui/src/renderer/components/AccountTokenLabel.tsx b/gui/src/renderer/components/AccountTokenLabel.tsx index 660d3f6578..d581749871 100644 --- a/gui/src/renderer/components/AccountTokenLabel.tsx +++ b/gui/src/renderer/components/AccountTokenLabel.tsx @@ -6,14 +6,12 @@ import ClipboardLabel from './ClipboardLabel'; interface IAccountTokenLabelProps { accountToken: string; style?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; - messageStyle?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; } export default function AccountTokenLabel(props: IAccountTokenLabelProps) { return ( <ClipboardLabel style={props.style} - messageStyle={props.messageStyle} value={props.accountToken} displayValue={formatAccountToken(props.accountToken)} /> diff --git a/gui/src/renderer/components/ClipboardLabel.tsx b/gui/src/renderer/components/ClipboardLabel.tsx index 56a716aeae..567c41e45c 100644 --- a/gui/src/renderer/components/ClipboardLabel.tsx +++ b/gui/src/renderer/components/ClipboardLabel.tsx @@ -8,7 +8,6 @@ interface IProps { delay: number; message: string; style?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; - messageStyle?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; } interface IState { @@ -35,11 +34,8 @@ export default class ClipboardLabel extends Component<IProps, IState> { public render() { const displayValue = this.props.displayValue || this.props.value; - const style = this.state.showsMessage - ? [this.props.style, this.props.messageStyle] - : this.props.style; return ( - <Text style={style} onPress={this.handlePress}> + <Text style={this.props.style} onPress={this.handlePress}> {this.state.showsMessage ? this.props.message : displayValue} </Text> ); diff --git a/gui/src/renderer/components/Connect.tsx b/gui/src/renderer/components/Connect.tsx index c994f4f42f..55bc49c53a 100644 --- a/gui/src/renderer/components/Connect.tsx +++ b/gui/src/renderer/components/Connect.tsx @@ -1,15 +1,11 @@ import * as React from 'react'; import { Component, Styles, View } from 'reactxp'; -import { links } from '../../config.json'; import AccountExpiry from '../../shared/account-expiry'; -import { AccountToken } from '../../shared/daemon-rpc-types'; -import NewAccountViewContainer from '../containers/NewAccountViewContainer'; +import ExpiredAccountErrorViewContainer from '../containers/ExpiredAccountErrorViewContainer'; import NotificationAreaContainer from '../containers/NotificationAreaContainer'; import { AuthFailureKind, parseAuthFailure } from '../lib/auth-failure'; import { LoginState } from '../redux/account/reducers'; import { IConnectionReduxState } from '../redux/connection/reducers'; -import { IVersionReduxState } from '../redux/version/reducers'; -import ExpiredAccountErrorView, { RecoveryAction } from './ExpiredAccountErrorView'; import { Brand, HeaderBarStyle, SettingsBarButton } from './HeaderBar'; import ImageView from './ImageView'; import { Container, Header, Layout } from './Layout'; @@ -19,18 +15,14 @@ import TunnelControl from './TunnelControl'; interface IProps { connection: IConnectionReduxState; - version: IVersionReduxState; - accountToken?: AccountToken; loginState: LoginState; accountExpiry?: AccountExpiry; selectedRelayName: string; - blockWhenDisconnected: boolean; onSettings: () => void; onSelectLocation: () => void; onConnect: () => void; onDisconnect: () => void; onReconnect: () => void; - onExternalLinkWithAuth: (url: string) => Promise<void>; } type MarkerOrSpinner = 'marker' | 'spinner'; @@ -87,7 +79,7 @@ export default class Connect extends Component<IProps, IState> { super(props); this.state = { - isAccountExpired: this.checkAccountExpired(props, false), + isAccountExpired: this.checkAccountExpired(false), }; } @@ -103,14 +95,22 @@ export default class Connect extends Component<IProps, IState> { <Brand /> <SettingsBarButton onPress={this.props.onSettings} /> </Header> - <Container>{this.renderContent()}</Container> + <Container> + {this.state.isAccountExpired || + (this.props.loginState.type === 'ok' && + this.props.loginState.method === 'new_account') ? ( + <ExpiredAccountErrorViewContainer /> + ) : ( + this.renderMap() + )} + </Container> </Layout> </ModalContainer> ); } private updateAccountExpired() { - const nextAccountExpired = this.checkAccountExpired(this.props, this.state.isAccountExpired); + const nextAccountExpired = this.checkAccountExpired(this.state.isAccountExpired); if (nextAccountExpired !== this.state.isAccountExpired) { this.setState({ @@ -119,8 +119,8 @@ export default class Connect extends Component<IProps, IState> { } } - private checkAccountExpired(props: IProps, prevAccountExpired: boolean): boolean { - const tunnelState = props.connection.status; + private checkAccountExpired(prevAccountExpired: boolean): boolean { + const tunnelState = this.props.connection.status; // Blocked with auth failure / expired account if ( @@ -141,22 +141,6 @@ export default class Connect extends Component<IProps, IState> { return prevAccountExpired; } - private renderContent() { - if (this.props.loginState.type === 'ok' && this.props.loginState.method === 'new_account') { - return <NewAccountViewContainer />; - } else if (this.state.isAccountExpired) { - return ( - <ExpiredAccountErrorView - blockWhenDisconnected={this.props.blockWhenDisconnected} - isBlocked={this.props.connection.isBlocked} - action={this.handleExpiredAccountRecovery} - /> - ); - } else { - return this.renderMap(); - } - } - private renderMap() { return ( <View style={styles.connect}> @@ -186,26 +170,6 @@ export default class Connect extends Component<IProps, IState> { ); } - private handleExpiredAccountRecovery = async (recoveryAction: RecoveryAction): Promise<void> => { - switch (recoveryAction) { - case RecoveryAction.disableBlockedWhenDisconnected: - break; - - case RecoveryAction.openBrowser: - await this.props.onExternalLinkWithAuth(links.purchase); - break; - - case RecoveryAction.disconnectAndOpenBrowser: - try { - await this.props.onDisconnect(); - await this.props.onExternalLinkWithAuth(links.purchase); - } catch (error) { - // no-op - } - break; - } - }; - private headerBarStyle(): HeaderBarStyle { const { status } = this.props.connection; switch (status.state) { diff --git a/gui/src/renderer/components/ExpiredAccountErrorView.tsx b/gui/src/renderer/components/ExpiredAccountErrorView.tsx index 1b0ceeb9f8..182fe6d193 100644 --- a/gui/src/renderer/components/ExpiredAccountErrorView.tsx +++ b/gui/src/renderer/components/ExpiredAccountErrorView.tsx @@ -1,174 +1,163 @@ import * as React from 'react'; -import { Component, Styles, View } from 'reactxp'; -import { colors } from '../../config.json'; +import { Component, Text, View } from 'reactxp'; +import { sprintf } from 'sprintf-js'; +import { links } from '../../config.json'; +import AccountExpiry from '../../shared/account-expiry'; +import { AccountToken } from '../../shared/daemon-rpc-types'; import { messages } from '../../shared/gettext'; +import { LoginState } from '../redux/account/reducers'; +import AccountTokenLabel from './AccountTokenLabel'; import * as AppButton from './AppButton'; +import styles from './ExpiredAccountErrorViewStyles'; import ImageView from './ImageView'; export enum RecoveryAction { openBrowser, - disconnectAndOpenBrowser, + disconnect, disableBlockedWhenDisconnected, } -interface IProps { +interface IExpiredAccountErrorViewProps { isBlocked: boolean; blockWhenDisconnected: boolean; - action: (recoveryAction: RecoveryAction) => Promise<void>; + accountToken?: AccountToken; + accountExpiry?: AccountExpiry; + loginState: LoginState; + hideWelcomeView: () => void; + onExternalLinkWithAuth: (url: string) => Promise<void>; + onDisconnect: () => Promise<void>; } -interface IState { - recoveryAction: RecoveryAction; -} - -const styles = { - container: Styles.createViewStyle({ - flex: 1, - paddingTop: 94, - }), - body: Styles.createViewStyle({ - flex: 1, - paddingHorizontal: 24, - }), - title: Styles.createTextStyle({ - fontFamily: 'DINPro', - fontSize: 32, - fontWeight: '900', - lineHeight: 40, - color: colors.white, - marginBottom: 8, - }), - message: Styles.createTextStyle({ - fontFamily: 'Open Sans', - fontSize: 13, - lineHeight: 20, - fontWeight: '600', - color: colors.white, - marginBottom: 24, - }), - statusIcon: Styles.createViewStyle({ - alignSelf: 'center', - width: 60, - height: 60, - marginBottom: 32, - }), -}; - -export default class ExpiredAccountErrorView extends Component<IProps, IState> { - public static getDerivedStateFromProps(props: IProps): IState { - const { blockWhenDisconnected, isBlocked } = props; - - if (blockWhenDisconnected && isBlocked) { - return { recoveryAction: RecoveryAction.disableBlockedWhenDisconnected }; - } else if (!blockWhenDisconnected && isBlocked) { - return { recoveryAction: RecoveryAction.disconnectAndOpenBrowser }; - } else { - return { recoveryAction: RecoveryAction.openBrowser }; +export default class ExpiredAccountErrorView extends Component<IExpiredAccountErrorViewProps> { + public componentDidUpdate() { + if (this.props.accountExpiry && !this.props.accountExpiry.hasExpired()) { + this.props.hideWelcomeView(); } } - public state: IState = { recoveryAction: RecoveryAction.openBrowser }; public render() { return ( <View style={styles.container}> - <View style={styles.statusIcon}> - <ImageView source="icon-fail" height={60} width={60} /> - </View> - <View style={styles.body}> - <View style={styles.title}>{messages.pgettext('connect-view', 'Out of time')}</View> - {this.renderContent()} + <View style={styles.body}>{this.renderContent()}</View> + + <View style={styles.footer}> + {this.getRecoveryAction() === RecoveryAction.disconnect && ( + <AppButton.BlockingButton onPress={this.props.onDisconnect}> + <AppButton.RedButton style={styles.button}> + {messages.pgettext('connect-view', 'Disconnect')} + </AppButton.RedButton> + </AppButton.BlockingButton> + )} + + {this.renderExternalPaymentButton()} </View> </View> ); } private renderContent() { - switch (this.state.recoveryAction) { - case RecoveryAction.disconnectAndOpenBrowser: - return <DisconnectAndOpenBrowserContentView actionHandler={this.handleAction} />; - case RecoveryAction.openBrowser: - return <OpenBrowserContentView actionHandler={this.handleAction} />; - case RecoveryAction.disableBlockedWhenDisconnected: - return <DisableBlockWhenDisconnectedContentView />; + if (this.isNewAccount()) { + return this.renderWelcomeView(); } - } - - private handleAction = (): Promise<void> => { - return this.props.action(this.state.recoveryAction); - }; -} -class DisconnectAndOpenBrowserContentView extends Component<{ - actionHandler: () => Promise<void>; -}> { - public render() { return ( - <View> - <View style={styles.message}> - {messages.pgettext( - 'connect-view', - 'You have no more VPN time left on this account. To buy more credit on our website, you will need to access the Internet with an unsecured connection.', - )} + <> + <View style={styles.statusIcon}> + <ImageView source="icon-fail" height={60} width={60} /> </View> - <View> - <AppButton.BlockingButton onPress={this.props.actionHandler}> - <AppButton.RedButton> - <AppButton.Label> - {messages.pgettext('connect-view', 'Disconnect and buy more credit')} - </AppButton.Label> - <AppButton.Icon source="icon-extLink" height={16} width={16} /> - </AppButton.RedButton> - </AppButton.BlockingButton> + <View style={styles.title}>{messages.pgettext('connect-view', 'Out of time')}</View> + <View style={styles.message}> + {sprintf('%(introduction)s %(recoveryMessage)s', { + introduction: messages.pgettext( + 'connect-view', + 'You have no more VPN time left on this account.', + ), + recoveryMessage: this.getRecoveryActionMessage(), + })} </View> - </View> + </> ); } -} -class OpenBrowserContentView extends Component<{ actionHandler: () => Promise<void> }> { - public render() { + private renderWelcomeView() { return ( - <View> - <View style={styles.message}> - {messages.pgettext( - 'connect-view', - 'You have no more VPN time left on this account. Please log in on our website to buy more credit.', - )} + <> + <View style={styles.title}>{messages.pgettext('connect-view', 'Congrats!')}</View> + <View style={[styles.message, styles.accountTokenMessage]}> + <Text style={[styles.fieldLabel, styles.accountTokenFieldLabel]}> + {messages.pgettext('connect-view', 'Here’s your account number. Save it!')} + </Text> + <View style={styles.accountTokenContainer}> + <AccountTokenLabel + style={styles.accountToken} + accountToken={this.props.accountToken || ''} + /> + </View> </View> - <View> - <AppButton.BlockingButton onPress={this.props.actionHandler}> - <AppButton.GreenButton> - <AppButton.Label> - {messages.pgettext('connect-view', 'Buy more credit')} - </AppButton.Label> - <AppButton.Icon source="icon-extLink" height={16} width={16} /> - </AppButton.GreenButton> - </AppButton.BlockingButton> + + <View style={styles.message}> + {sprintf('%(introduction)s %(recoveryMessage)s', { + introduction: messages.pgettext( + 'connect-view', + 'To start using the app, you first need to add time to your account.', + ), + recoveryMessage: this.getRecoveryActionMessage(), + })} </View> - </View> + </> ); } -} -class DisableBlockWhenDisconnectedContentView extends Component { - public render() { + private getRecoveryActionMessage() { + switch (this.getRecoveryAction()) { + case RecoveryAction.openBrowser: + case RecoveryAction.disableBlockedWhenDisconnected: + return messages.pgettext( + 'connect-view', + 'Either buy credit on our website or redeem a voucher.', + ); + case RecoveryAction.disconnect: + return messages.pgettext( + 'connect-view', + 'To add more, you will need to disconnect and access the Internet with an unsecured connection.', + ); + } + } + + private renderExternalPaymentButton() { + const buttonText = this.isNewAccount() + ? messages.pgettext('connect-view', 'Buy credit') + : messages.pgettext('connect-view', 'Buy more credit'); + return ( - <View> - <View style={styles.message}> - {messages.pgettext( - 'connect-view', - 'You have no more VPN time left on this account. Before you can buy more credit on our website, you first need to turn off the app\'s "Block when disconnected" option under Advanced settings.', - )} - </View> - <View> - <AppButton.GreenButton disabled={true}> - <AppButton.Label> - {messages.pgettext('connect-view', 'Buy more credit')} - </AppButton.Label> - <AppButton.Icon source="icon-extLink" height={16} width={16} /> - </AppButton.GreenButton> - </View> - </View> + <AppButton.BlockingButton + disabled={this.props.isBlocked} + onPress={this.onOpenExternalPayment}> + <AppButton.GreenButton> + <AppButton.Label>{buttonText}</AppButton.Label> + <AppButton.Icon source="icon-extLink" height={16} width={16} /> + </AppButton.GreenButton> + </AppButton.BlockingButton> ); } + + private isNewAccount() { + return this.props.loginState.type === 'ok' && this.props.loginState.method === 'new_account'; + } + + private onOpenExternalPayment = async (): Promise<void> => { + await this.props.onExternalLinkWithAuth(links.purchase); + }; + + private getRecoveryAction() { + const { blockWhenDisconnected, isBlocked } = this.props; + + if (blockWhenDisconnected && isBlocked) { + return RecoveryAction.disableBlockedWhenDisconnected; + } else if (!blockWhenDisconnected && isBlocked) { + return RecoveryAction.disconnect; + } else { + return RecoveryAction.openBrowser; + } + } } diff --git a/gui/src/renderer/components/NewAccountViewStyles.tsx b/gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx index d7a6407789..93535a043b 100644 --- a/gui/src/renderer/components/NewAccountViewStyles.tsx +++ b/gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx @@ -4,11 +4,11 @@ import { colors } from '../../config.json'; export default { container: Styles.createViewStyle({ flex: 1, + paddingTop: 24, }), body: Styles.createViewStyle({ flex: 1, paddingHorizontal: 24, - marginTop: 20, }), footer: Styles.createViewStyle({ flex: 0, @@ -19,8 +19,8 @@ export default { title: Styles.createTextStyle({ fontFamily: 'DINPro', fontSize: 32, - lineHeight: 40, fontWeight: '900', + lineHeight: 40, color: colors.white, marginBottom: 8, }), @@ -32,12 +32,11 @@ export default { color: colors.white, marginBottom: 24, }), - accountToken: Styles.createTextStyle({ - fontFamily: 'Open Sans', - fontSize: 24, - fontWeight: '800', - color: colors.white, - marginTop: 15, + statusIcon: Styles.createViewStyle({ + alignSelf: 'center', + width: 60, + height: 60, + marginBottom: 18, }), fieldLabel: Styles.createTextStyle({ fontFamily: 'Open Sans', @@ -47,12 +46,24 @@ export default { color: colors.white, marginBottom: 9, }), - or: Styles.createTextStyle({ - fontSize: 20, + accountTokenMessage: Styles.createViewStyle({ + marginBottom: 0, + }), + accountTokenFieldLabel: Styles.createTextStyle({ + marginBottom: 0, + }), + accountTokenContainer: Styles.createViewStyle({ + height: 68, + justifyContent: 'center', + }), + accountToken: Styles.createTextStyle({ + fontFamily: 'Open Sans', lineHeight: 24, + fontSize: 24, fontWeight: '800', - textAlign: 'center', color: colors.white, - marginVertical: 12, + }), + button: Styles.createViewStyle({ + marginBottom: 24, }), }; diff --git a/gui/src/renderer/components/NewAccountView.tsx b/gui/src/renderer/components/NewAccountView.tsx deleted file mode 100644 index a4a51191fb..0000000000 --- a/gui/src/renderer/components/NewAccountView.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from 'react'; -import { Component, Text, View } from 'reactxp'; -import { links } from '../../config.json'; -import AccountExpiry from '../../shared/account-expiry'; -import { AccountToken } from '../../shared/daemon-rpc-types'; -import { messages } from '../../shared/gettext'; -import styles from './NewAccountViewStyles'; -import AccountTokenLabel from './AccountTokenLabel'; -import * as AppButton from './AppButton'; - -interface INewAccountViewProps { - accountToken?: AccountToken; - accountExpiry?: AccountExpiry; - hideWelcomeView: () => void; - onExternalLinkWithAuth: (url: string) => Promise<void>; -} - -export default class NewAccountView extends Component<INewAccountViewProps> { - public componentDidUpdate() { - if (this.props.accountExpiry && !this.props.accountExpiry.hasExpired()) { - this.props.hideWelcomeView(); - } - } - - public render() { - return ( - <View style={styles.container}> - <View style={styles.body}> - <View style={styles.title}>{messages.pgettext('new-account-view', 'Congrats!')}</View> - <View style={styles.message}> - <Text style={styles.fieldLabel}> - {messages.pgettext('new-account-view', "Here's your account number! Save it!")} - </Text> - <AccountTokenLabel - style={styles.accountToken} - accountToken={this.props.accountToken || ''} - /> - </View> - - <View style={styles.message}> - {messages.pgettext( - 'new-account-view', - 'To start using the app you first need to add time to you account. You can either buy it online or redeem a voucher if you have one.', - )} - </View> - </View> - {this.createFooter()} - </View> - ); - } - - private createFooter() { - return ( - <View style={styles.footer}> - <AppButton.BlockingButton onPress={this.openPaymentUrl}> - <AppButton.GreenButton> - <AppButton.Label>{messages.pgettext('new-account-view', 'Buy online')}</AppButton.Label> - <AppButton.Icon source="icon-extLink" height={16} width={16} /> - </AppButton.GreenButton> - </AppButton.BlockingButton> - </View> - ); - } - - private openPaymentUrl = async (): Promise<void> => { - await this.props.onExternalLinkWithAuth(links.purchase); - }; -} diff --git a/gui/src/renderer/containers/ConnectPage.tsx b/gui/src/renderer/containers/ConnectPage.tsx index e9aab2d8b3..06e3713fe8 100644 --- a/gui/src/renderer/containers/ConnectPage.tsx +++ b/gui/src/renderer/containers/ConnectPage.tsx @@ -68,12 +68,9 @@ const mapStateToProps = (state: IReduxState) => { accountExpiry: state.account.expiry ? new AccountExpiry(state.account.expiry, state.userInterface.locale) : undefined, - accountToken: state.account.accountToken, loginState: state.account.status, selectedRelayName: getRelayName(state.settings.relaySettings, state.settings.relayLocations), connection: state.connection, - version: state.version, - blockWhenDisconnected: state.settings.blockWhenDisconnected, }; }; @@ -108,7 +105,6 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => { log.error(`Failed to reconnect the tunnel: ${error.message}`); } }, - onExternalLinkWithAuth: (url: string) => props.app.openLinkWithAuth(url), }; }; diff --git a/gui/src/renderer/containers/NewAccountViewContainer.tsx b/gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx index a174874d48..96e97f1e22 100644 --- a/gui/src/renderer/containers/NewAccountViewContainer.tsx +++ b/gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx @@ -1,7 +1,8 @@ +import log from 'electron-log'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import AccountExpiry from '../../shared/account-expiry'; -import NewAccountView from '../components/NewAccountView'; +import ExpiredAccountErrorView from '../components/ExpiredAccountErrorView'; import accountActions from '../redux/account/actions'; import withAppContext, { IAppContext } from '../context'; @@ -12,7 +13,9 @@ const mapStateToProps = (state: IReduxState) => ({ accountExpiry: state.account.expiry ? new AccountExpiry(state.account.expiry, state.userInterface.locale) : undefined, - isOffline: state.connection.isBlocked, + loginState: state.account.status, + isBlocked: state.connection.isBlocked, + blockWhenDisconnected: state.settings.blockWhenDisconnected, }); const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => { const account = bindActionCreators(accountActions, dispatch); @@ -20,7 +23,16 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => { // Changes login method from "new_account" to "existing_account" hideWelcomeView: () => account.loggedIn(), onExternalLinkWithAuth: (url: string) => props.app.openLinkWithAuth(url), + onDisconnect: async () => { + try { + await props.app.disconnectTunnel(); + } catch (error) { + log.error(`Failed to disconnect the tunnel: ${error.message}`); + } + }, }; }; -export default withAppContext(connect(mapStateToProps, mapDispatchToProps)(NewAccountView)); +export default withAppContext( + connect(mapStateToProps, mapDispatchToProps)(ExpiredAccountErrorView), +); |
