summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-02-27 22:16:34 +0100
committerOskar Nyberg <oskar@mullvad.net>2020-04-06 09:38:47 +0200
commit808d2f753c64da3a3e5f49bb0fdf52767faa2e30 (patch)
treec3d4c33839b5f24affd091c8097e87e12838ecc9 /gui/src
parent72415b7193772224b88d315593d64354a1677657 (diff)
downloadmullvadvpn-808d2f753c64da3a3e5f49bb0fdf52767faa2e30.tar.xz
mullvadvpn-808d2f753c64da3a3e5f49bb0fdf52767faa2e30.zip
Add view shown after creating account
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/AccountTokenLabel.tsx2
-rw-r--r--gui/src/renderer/components/ClipboardLabel.tsx6
-rw-r--r--gui/src/renderer/components/Connect.tsx51
-rw-r--r--gui/src/renderer/components/Modal.tsx8
-rw-r--r--gui/src/renderer/components/NewAccountView.tsx59
-rw-r--r--gui/src/renderer/components/NewAccountViewStyles.tsx58
-rw-r--r--gui/src/renderer/containers/ConnectPage.tsx2
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,