diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-02-27 22:16:34 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-04-06 09:38:47 +0200 |
| commit | 808d2f753c64da3a3e5f49bb0fdf52767faa2e30 (patch) | |
| tree | c3d4c33839b5f24affd091c8097e87e12838ecc9 | |
| parent | 72415b7193772224b88d315593d64354a1677657 (diff) | |
| download | mullvadvpn-808d2f753c64da3a3e5f49bb0fdf52767faa2e30.tar.xz mullvadvpn-808d2f753c64da3a3e5f49bb0fdf52767faa2e30.zip | |
Add view shown after creating account
| -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 | 51 | ||||
| -rw-r--r-- | gui/src/renderer/components/Modal.tsx | 8 | ||||
| -rw-r--r-- | gui/src/renderer/components/NewAccountView.tsx | 59 | ||||
| -rw-r--r-- | gui/src/renderer/components/NewAccountViewStyles.tsx | 58 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ConnectPage.tsx | 2 |
7 files changed, 166 insertions, 20 deletions
diff --git a/gui/src/renderer/components/AccountTokenLabel.tsx b/gui/src/renderer/components/AccountTokenLabel.tsx index d581749871..660d3f6578 100644 --- a/gui/src/renderer/components/AccountTokenLabel.tsx +++ b/gui/src/renderer/components/AccountTokenLabel.tsx @@ -6,12 +6,14 @@ 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 567c41e45c..56a716aeae 100644 --- a/gui/src/renderer/components/ClipboardLabel.tsx +++ b/gui/src/renderer/components/ClipboardLabel.tsx @@ -8,6 +8,7 @@ interface IProps { delay: number; message: string; style?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; + messageStyle?: Types.StyleRuleSetRecursive<Types.TextStyleRuleSet>; } interface IState { @@ -34,8 +35,11 @@ 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={this.props.style} onPress={this.handlePress}> + <Text style={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 ebc818e767..7a6ba17739 100644 --- a/gui/src/renderer/components/Connect.tsx +++ b/gui/src/renderer/components/Connect.tsx @@ -2,8 +2,10 @@ 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 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'; @@ -11,11 +13,15 @@ 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'; +import NewAccountView from './NewAccountView'; interface IProps { connection: IConnectionReduxState; version: IVersionReduxState; + accountToken?: AccountToken; + loginState: LoginState; accountExpiry?: AccountExpiry; selectedRelayName: string; blockWhenDisconnected: boolean; @@ -91,15 +97,15 @@ 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.renderContent()}</Container> + </Layout> + </ModalContainer> ); } @@ -135,14 +141,25 @@ 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 renderContent() { + if (this.props.loginState.type === 'ok' && this.props.loginState.method === 'new_account') { + return ( + <NewAccountView + accountToken={this.props.accountToken} + onExternalLinkWithAuth={this.props.onExternalLinkWithAuth} + /> + ); + } 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() { 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/components/NewAccountView.tsx b/gui/src/renderer/components/NewAccountView.tsx new file mode 100644 index 0000000000..e6319a3d0e --- /dev/null +++ b/gui/src/renderer/components/NewAccountView.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { Component, Text, View } from 'reactxp'; +import { links } from '../../config.json'; +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; + onExternalLinkWithAuth: (url: string) => Promise<void>; +} + +export default class NewAccountView extends Component<INewAccountViewProps> { + 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/components/NewAccountViewStyles.tsx b/gui/src/renderer/components/NewAccountViewStyles.tsx new file mode 100644 index 0000000000..d7a6407789 --- /dev/null +++ b/gui/src/renderer/components/NewAccountViewStyles.tsx @@ -0,0 +1,58 @@ +import { Styles } from 'reactxp'; +import { colors } from '../../config.json'; + +export default { + container: Styles.createViewStyle({ + flex: 1, + }), + body: Styles.createViewStyle({ + flex: 1, + paddingHorizontal: 24, + marginTop: 20, + }), + footer: Styles.createViewStyle({ + flex: 0, + paddingVertical: 24, + paddingHorizontal: 24, + backgroundColor: colors.darkBlue, + }), + title: Styles.createTextStyle({ + fontFamily: 'DINPro', + fontSize: 32, + lineHeight: 40, + fontWeight: '900', + color: colors.white, + marginBottom: 8, + }), + message: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 13, + lineHeight: 20, + fontWeight: '600', + color: colors.white, + marginBottom: 24, + }), + accountToken: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 24, + fontWeight: '800', + color: colors.white, + marginTop: 15, + }), + fieldLabel: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 13, + fontWeight: '600', + lineHeight: 20, + color: colors.white, + marginBottom: 9, + }), + or: Styles.createTextStyle({ + fontSize: 20, + lineHeight: 24, + fontWeight: '800', + textAlign: 'center', + color: colors.white, + marginVertical: 12, + }), +}; diff --git a/gui/src/renderer/containers/ConnectPage.tsx b/gui/src/renderer/containers/ConnectPage.tsx index 97eb499fab..e9aab2d8b3 100644 --- a/gui/src/renderer/containers/ConnectPage.tsx +++ b/gui/src/renderer/containers/ConnectPage.tsx @@ -68,6 +68,8 @@ 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, |
