diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-04-06 11:03:15 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-04-06 11:03:15 +0200 |
| commit | 9894c710edf64d9bfe9f53746b3d885780197c73 (patch) | |
| tree | 8c5cf3423b90c990672d774abe5b0b11e7921058 /gui/src | |
| parent | 72415b7193772224b88d315593d64354a1677657 (diff) | |
| parent | d326e5088a6898aa3605a853aad53daba7173024 (diff) | |
| download | mullvadvpn-9894c710edf64d9bfe9f53746b3d885780197c73.tar.xz mullvadvpn-9894c710edf64d9bfe9f53746b3d885780197c73.zip | |
Merge branch 'add-new-account-view'
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/main/account-data-cache.ts | 15 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/Connect.tsx | 74 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorView.tsx | 295 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx | 69 | ||||
| -rw-r--r-- | gui/src/renderer/components/Modal.tsx | 8 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ConnectPage.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx | 45 |
8 files changed, 334 insertions, 180 deletions
diff --git a/gui/src/main/account-data-cache.ts b/gui/src/main/account-data-cache.ts index 97a94eb7c3..7db2c05ca8 100644 --- a/gui/src/main/account-data-cache.ts +++ b/gui/src/main/account-data-cache.ts @@ -1,7 +1,10 @@ import log from 'electron-log'; +import moment from 'moment'; import { AccountToken, IAccountData } from '../shared/daemon-rpc-types'; import consumePromise from '../shared/promise'; +const EXPIRED_ACCOUNT_REFRESH_PERIOD = 60_000; + export enum AccountFetchRetryAction { stop, retry, @@ -77,6 +80,7 @@ export default class AccountDataCache { if (this.currentAccount === accountToken) { this.setValue(accountData); + this.scheduleRefetchIfExpired(accountToken, accountData); } } catch (error) { if (this.currentAccount === accountToken) { @@ -85,6 +89,13 @@ export default class AccountDataCache { } } + private scheduleRefetchIfExpired(accountToken: AccountToken, accountData: IAccountData) { + const hasExpired = moment(accountData.expiry).isSameOrBefore(new Date()); + if (hasExpired) { + this.scheduleFetch(accountToken, EXPIRED_ACCOUNT_REFRESH_PERIOD); + } + } + private handleFetchError(accountToken: AccountToken, error: Error) { let shouldRetry = true; @@ -107,6 +118,10 @@ export default class AccountDataCache { log.warn(`Failed to fetch account data. Retrying in ${delay} ms`); + this.scheduleFetch(accountToken, delay); + } + + private scheduleFetch(accountToken: AccountToken, delay: number) { this.fetchRetryTimeout = global.setTimeout(() => { this.fetchRetryTimeout = undefined; consumePromise(this.performFetch(accountToken)); diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index 01aec17d62..28956d8c88 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -901,7 +901,7 @@ class ApplicationMain { // cancel notifications when window appears this.notificationController.cancelPendingNotifications(); - this.updateAccountExpiryIfNeeded(); + this.updateAccountData(); }); windowController.window.on('hide', () => { @@ -1159,7 +1159,7 @@ class ApplicationMain { } } - private updateAccountExpiryIfNeeded() { + private updateAccountData() { if (this.connectedToDaemon && this.settings.accountToken) { this.accountDataCache.fetch(this.settings.accountToken); } diff --git a/gui/src/renderer/components/Connect.tsx b/gui/src/renderer/components/Connect.tsx index ebc818e767..55bc49c53a 100644 --- a/gui/src/renderer/components/Connect.tsx +++ b/gui/src/renderer/components/Connect.tsx @@ -1,30 +1,28 @@ import * as React from 'react'; import { Component, Styles, View } from 'reactxp'; -import { links } from '../../config.json'; import AccountExpiry from '../../shared/account-expiry'; +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'; import Map, { MarkerStyle, ZoomLevel } from './Map'; +import { ModalContainer } from './Modal'; import TunnelControl from './TunnelControl'; interface IProps { connection: IConnectionReduxState; - version: IVersionReduxState; + 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'; @@ -81,7 +79,7 @@ export default class Connect extends Component<IProps, IState> { super(props); this.state = { - isAccountExpired: this.checkAccountExpired(props, false), + isAccountExpired: this.checkAccountExpired(false), }; } @@ -91,20 +89,28 @@ export default class Connect extends Component<IProps, IState> { public render() { return ( - <Layout> - <Header barStyle={this.headerBarStyle()}> - <Brand /> - <SettingsBarButton onPress={this.props.onSettings} /> - </Header> - <Container> - {this.state.isAccountExpired ? this.renderExpiredAccountView() : this.renderMap()} - </Container> - </Layout> + <ModalContainer> + <Layout> + <Header barStyle={this.headerBarStyle()}> + <Brand /> + <SettingsBarButton onPress={this.props.onSettings} /> + </Header> + <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({ @@ -113,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 ( @@ -135,16 +141,6 @@ export default class Connect extends Component<IProps, IState> { return prevAccountExpired; } - private renderExpiredAccountView() { - return ( - <ExpiredAccountErrorView - blockWhenDisconnected={this.props.blockWhenDisconnected} - isBlocked={this.props.connection.isBlocked} - action={this.handleExpiredAccountRecovery} - /> - ); - } - private renderMap() { return ( <View style={styles.connect}> @@ -174,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..7f50d7340f 100644 --- a/gui/src/renderer/components/ExpiredAccountErrorView.tsx +++ b/gui/src/renderer/components/ExpiredAccountErrorView.tsx @@ -1,174 +1,221 @@ 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 * as Cell from './Cell'; +import styles from './ExpiredAccountErrorViewStyles'; import ImageView from './ImageView'; +import { ModalAlert, ModalAlertType } from './Modal'; 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>; + setBlockWhenDisconnected: (value: boolean) => void; } -interface IState { - recoveryAction: RecoveryAction; +interface IExpiredAccountErrorViewState { + showBlockWhenDisconnectedAlert: boolean; } -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; +export default class ExpiredAccountErrorView extends Component< + IExpiredAccountErrorViewProps, + IExpiredAccountErrorViewState +> { + public state: IExpiredAccountErrorViewState = { + showBlockWhenDisconnectedAlert: false, + }; - if (blockWhenDisconnected && isBlocked) { - return { recoveryAction: RecoveryAction.disableBlockedWhenDisconnected }; - } else if (!blockWhenDisconnected && isBlocked) { - return { recoveryAction: RecoveryAction.disconnectAndOpenBrowser }; - } else { - return { recoveryAction: RecoveryAction.openBrowser }; + 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> + + {this.state.showBlockWhenDisconnectedAlert && this.renderBlockWhenDisconnectedAlert()} </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}> + <AppButton.BlockingButton + disabled={this.getRecoveryAction() === RecoveryAction.disconnect} + 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 renderBlockWhenDisconnectedAlert() { + return ( + <ModalAlert + type={ModalAlertType.Info} + buttons={[ + <AppButton.BlueButton + key="cancel" + onPress={this.onCloseBlockWhenDisconnectedInstructions}> + {messages.pgettext('connect-view', 'Close')} + </AppButton.BlueButton>, + ]}> + <Text style={styles.fieldLabel}> {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.', + 'You need to disable “Block when disconnected” in order to access the Internet to add time.', )} - </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> + </Text> + <Text style={styles.fieldLabel}> + {messages.pgettext( + 'connect-view', + 'Remember, turning it off will allow network traffic while the VPN is disconnected until you turn it back on under Advanced settings.', + )} + </Text> + <Cell.Container> + <Cell.Label>{messages.pgettext('connect-view', 'Block when disconnected')}</Cell.Label> + <Cell.Switch + isOn={this.props.blockWhenDisconnected} + onChange={this.props.setBlockWhenDisconnected} + /> + </Cell.Container> + </ModalAlert> ); } + + private onOpenExternalPayment = async (): Promise<void> => { + if (this.getRecoveryAction() === RecoveryAction.disableBlockedWhenDisconnected) { + this.setState({ showBlockWhenDisconnectedAlert: true }); + } else { + 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; + } + } + + private onCloseBlockWhenDisconnectedInstructions = () => { + this.setState({ showBlockWhenDisconnectedAlert: false }); + }; } diff --git a/gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx b/gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx new file mode 100644 index 0000000000..93535a043b --- /dev/null +++ b/gui/src/renderer/components/ExpiredAccountErrorViewStyles.tsx @@ -0,0 +1,69 @@ +import { Styles } from 'reactxp'; +import { colors } from '../../config.json'; + +export default { + container: Styles.createViewStyle({ + flex: 1, + paddingTop: 24, + }), + body: Styles.createViewStyle({ + flex: 1, + paddingHorizontal: 24, + }), + footer: Styles.createViewStyle({ + flex: 0, + paddingVertical: 24, + paddingHorizontal: 24, + backgroundColor: colors.darkBlue, + }), + 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: 18, + }), + fieldLabel: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 13, + fontWeight: '600', + lineHeight: 20, + color: colors.white, + marginBottom: 9, + }), + 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', + color: colors.white, + }), + button: Styles.createViewStyle({ + marginBottom: 24, + }), +}; diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx index db7aca982b..b4c6ace833 100644 --- a/gui/src/renderer/components/Modal.tsx +++ b/gui/src/renderer/components/Modal.tsx @@ -99,8 +99,9 @@ export enum ModalAlertType { interface IModalAlertProps { type?: ModalAlertType; - message: string; + message?: string; buttons: React.ReactNode[]; + children?: React.ReactNode; } export class ModalAlert extends Component<IModalAlertProps> { @@ -121,7 +122,10 @@ export class ModalAlert extends Component<IModalAlertProps> { {this.props.type && ( <View style={styles.modalAlertIcon}>{this.renderTypeIcon(this.props.type)}</View> )} - <Text style={styles.modalAlertMessage}>{this.props.message}</Text> + {this.props.message && ( + <Text style={styles.modalAlertMessage}>{this.props.message}</Text> + )} + {this.props.children} {this.props.buttons.map((button, index) => ( <View key={index} style={styles.modalAlertButtonContainer}> {button} diff --git a/gui/src/renderer/containers/ConnectPage.tsx b/gui/src/renderer/containers/ConnectPage.tsx index 97eb499fab..06e3713fe8 100644 --- a/gui/src/renderer/containers/ConnectPage.tsx +++ b/gui/src/renderer/containers/ConnectPage.tsx @@ -68,10 +68,9 @@ const mapStateToProps = (state: IReduxState) => { accountExpiry: state.account.expiry ? new AccountExpiry(state.account.expiry, state.userInterface.locale) : undefined, + loginState: state.account.status, selectedRelayName: getRelayName(state.settings.relaySettings, state.settings.relayLocations), connection: state.connection, - version: state.version, - blockWhenDisconnected: state.settings.blockWhenDisconnected, }; }; @@ -106,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/ExpiredAccountErrorViewContainer.tsx b/gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx new file mode 100644 index 0000000000..2c4ca2e6dd --- /dev/null +++ b/gui/src/renderer/containers/ExpiredAccountErrorViewContainer.tsx @@ -0,0 +1,45 @@ +import log from 'electron-log'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import AccountExpiry from '../../shared/account-expiry'; +import ExpiredAccountErrorView from '../components/ExpiredAccountErrorView'; +import accountActions from '../redux/account/actions'; + +import withAppContext, { IAppContext } from '../context'; +import { IReduxState, ReduxDispatch } from '../redux/store'; + +const mapStateToProps = (state: IReduxState) => ({ + accountToken: state.account.accountToken, + accountExpiry: state.account.expiry + ? new AccountExpiry(state.account.expiry, state.userInterface.locale) + : undefined, + loginState: state.account.status, + isBlocked: state.connection.isBlocked, + blockWhenDisconnected: state.settings.blockWhenDisconnected, +}); +const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => { + const account = bindActionCreators(accountActions, dispatch); + return { + // 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}`); + } + }, + setBlockWhenDisconnected: async (blockWhenDisconnected: boolean) => { + try { + await props.app.setBlockWhenDisconnected(blockWhenDisconnected); + } catch (e) { + log.error('Failed to update block when disconnected', e.message); + } + }, + }; +}; + +export default withAppContext( + connect(mapStateToProps, mapDispatchToProps)(ExpiredAccountErrorView), +); |
