summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-12-08 12:46:42 -0200
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-12-11 12:38:23 -0200
commit22bb2777d95b79242f92c3ea06b72f087849e7d8 (patch)
tree2c27af9b30e2eb7a5c83910f215e76700929dd8d
parent6cc5b2badae95ed840bfad3c1ba3a17efa82608a (diff)
downloadmullvadvpn-22bb2777d95b79242f92c3ea06b72f087849e7d8.tar.xz
mullvadvpn-22bb2777d95b79242f92c3ea06b72f087849e7d8.zip
Implement warning when little account time remains
-rw-r--r--gui/packages/desktop/src/renderer/components/Connect.js1
-rw-r--r--gui/packages/desktop/src/renderer/components/NotificationArea.js34
-rw-r--r--gui/packages/desktop/src/renderer/lib/account-expiry.js6
-rw-r--r--gui/packages/desktop/test/components/NotificationArea.spec.js15
4 files changed, 53 insertions, 3 deletions
diff --git a/gui/packages/desktop/src/renderer/components/Connect.js b/gui/packages/desktop/src/renderer/components/Connect.js
index 852d7c053a..577f4e470a 100644
--- a/gui/packages/desktop/src/renderer/components/Connect.js
+++ b/gui/packages/desktop/src/renderer/components/Connect.js
@@ -199,6 +199,7 @@ export default class Connect extends Component<Props> {
style={styles.notification_area}
tunnelState={this.props.connection.status}
version={this.props.version}
+ accountExpiry={this.props.accountExpiry}
openExternalLink={this.props.onExternalLink}
blockWhenDisconnected={this.props.blockWhenDisconnected}
/>
diff --git a/gui/packages/desktop/src/renderer/components/NotificationArea.js b/gui/packages/desktop/src/renderer/components/NotificationArea.js
index dac5b916f6..d31308beed 100644
--- a/gui/packages/desktop/src/renderer/components/NotificationArea.js
+++ b/gui/packages/desktop/src/renderer/components/NotificationArea.js
@@ -1,4 +1,6 @@
// @flow
+
+import moment from 'moment';
import * as React from 'react';
import { Component, Types } from 'reactxp';
import {
@@ -12,11 +14,13 @@ import {
} from './NotificationBanner';
import { AuthFailure } from '../lib/auth-failure';
+import AccountExpiry from '../lib/account-expiry';
import type { BlockReason, TunnelStateTransition } from '../lib/daemon-rpc-proxy';
import type { VersionReduxState } from '../redux/version/reducers';
type Props = {
style?: Types.ViewStyleRuleSet,
+ accountExpiry: ?AccountExpiry,
tunnelState: TunnelStateTransition,
version: VersionReduxState,
openExternalLink: (string) => void,
@@ -27,7 +31,8 @@ type NotificationAreaPresentation =
| { type: 'blocking', reason: string }
| { type: 'inconsistent-version' }
| { type: 'unsupported-version', upgradeVersion: string }
- | { type: 'update-available', upgradeVersion: string };
+ | { type: 'update-available', upgradeVersion: string }
+ | { type: 'expires-soon', timeLeft: string };
type State = NotificationAreaPresentation & {
visible: boolean,
@@ -63,7 +68,7 @@ export default class NotificationArea extends Component<Props, State> {
};
static getDerivedStateFromProps(props: Props, state: State) {
- const { version, tunnelState, blockWhenDisconnected } = props;
+ const { accountExpiry, blockWhenDisconnected, tunnelState, version } = props;
switch (tunnelState.state) {
case 'connecting':
@@ -124,6 +129,14 @@ export default class NotificationArea extends Component<Props, State> {
};
}
+ if (accountExpiry && accountExpiry.willHaveExpiredIn(moment().add(3, 'days'))) {
+ return {
+ visible: true,
+ type: 'expires-soon',
+ timeLeft: accountExpiry.remainingTime(),
+ };
+ }
+
return {
...state,
visible: false,
@@ -193,6 +206,23 @@ export default class NotificationArea extends Component<Props, State> {
</NotificationActions>
</React.Fragment>
)}
+
+ {this.state.type === 'expires-soon' && (
+ <React.Fragment>
+ <NotificationIndicator type={'warning'} />
+ <NotificationContent>
+ <NotificationTitle>{'ACCOUNT CREDIT EXPIRES SOON'}</NotificationTitle>
+ <NotificationSubtitle>{this.state.timeLeft}</NotificationSubtitle>
+ </NotificationContent>
+ <NotificationActions>
+ <NotificationOpenLinkAction
+ onPress={() => {
+ this.props.openExternalLink('purchase');
+ }}
+ />
+ </NotificationActions>
+ </React.Fragment>
+ )}
</NotificationBanner>
);
}
diff --git a/gui/packages/desktop/src/renderer/lib/account-expiry.js b/gui/packages/desktop/src/renderer/lib/account-expiry.js
index b3ecf0b665..af240c2039 100644
--- a/gui/packages/desktop/src/renderer/lib/account-expiry.js
+++ b/gui/packages/desktop/src/renderer/lib/account-expiry.js
@@ -10,7 +10,11 @@ export default class AccountExpiry {
}
hasExpired(): boolean {
- return this._expiry.isSameOrBefore(moment());
+ return this.willHaveExpiredIn(moment());
+ }
+
+ willHaveExpiredIn(time: moment): boolean {
+ return this._expiry.isSameOrBefore(time);
}
remainingTime(): string {
diff --git a/gui/packages/desktop/test/components/NotificationArea.spec.js b/gui/packages/desktop/test/components/NotificationArea.spec.js
index bdd001e8ab..08639e36ff 100644
--- a/gui/packages/desktop/test/components/NotificationArea.spec.js
+++ b/gui/packages/desktop/test/components/NotificationArea.spec.js
@@ -3,6 +3,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import NotificationArea from '../../src/renderer/components/NotificationArea';
+import AccountExpiry from '../../src/renderer/lib/account-expiry';
describe('components/NotificationArea', () => {
const defaultVersion = {
@@ -15,6 +16,12 @@ describe('components/NotificationArea', () => {
nextUpgrade: null,
};
+ const defaultExpiry = new AccountExpiry(
+ moment()
+ .add(1, 'year')
+ .format(),
+ );
+
it('handles disconnecting state', () => {
for (const reason of ['nothing', 'block', 'reconnect']) {
const component = shallow(
@@ -24,6 +31,7 @@ describe('components/NotificationArea', () => {
details: { reason },
}}
version={defaultVersion}
+ accountExpiry={defaultExpiry}
/>,
);
expect(component.state('visible')).to.be.false;
@@ -38,6 +46,7 @@ describe('components/NotificationArea', () => {
state,
}}
version={defaultVersion}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -52,6 +61,7 @@ describe('components/NotificationArea', () => {
state: 'connecting',
}}
version={defaultVersion}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -69,6 +79,7 @@ describe('components/NotificationArea', () => {
},
}}
version={defaultVersion}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -86,6 +97,7 @@ describe('components/NotificationArea', () => {
...defaultVersion,
consistent: false,
}}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -106,6 +118,7 @@ describe('components/NotificationArea', () => {
current: '2018.1',
nextUpgrade: '2018.2',
}}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -127,6 +140,7 @@ describe('components/NotificationArea', () => {
latestStable: '2018.3',
nextUpgrade: '2018.3',
}}
+ accountExpiry={defaultExpiry}
/>,
);
@@ -149,6 +163,7 @@ describe('components/NotificationArea', () => {
latestStable: '2018.3',
nextUpgrade: '2018.4-beta3',
}}
+ accountExpiry={defaultExpiry}
/>,
);