summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-02-07 18:08:18 +0100
committerOskar Nyberg <oskar@mullvad.net>2022-02-09 12:51:56 +0100
commit4998b06e1516b1724ae0dc08576a553772b92565 (patch)
treee0b43819a7d9d67d416e580c8450facc3b24e451 /gui/src
parentbfd238f74448195f3d515dd2045678a2e9b7821a (diff)
downloadmullvadvpn-4998b06e1516b1724ae0dc08576a553772b92565.tar.xz
mullvadvpn-4998b06e1516b1724ae0dc08576a553772b92565.zip
Show correct time added information
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/ExpiredAccountAddTime.tsx59
-rw-r--r--gui/src/renderer/lib/history.tsx28
-rw-r--r--gui/src/renderer/lib/routes.ts13
3 files changed, 68 insertions, 32 deletions
diff --git a/gui/src/renderer/components/ExpiredAccountAddTime.tsx b/gui/src/renderer/components/ExpiredAccountAddTime.tsx
index 49e8bf8107..4c6c7989d7 100644
--- a/gui/src/renderer/components/ExpiredAccountAddTime.tsx
+++ b/gui/src/renderer/components/ExpiredAccountAddTime.tsx
@@ -1,16 +1,16 @@
import { useCallback } from 'react';
-import { useSelector } from 'react-redux';
+import { useSelector } from '../redux/store';
import { sprintf } from 'sprintf-js';
import styled from 'styled-components';
import { links, colors } from '../../config.json';
+import { formatDate } from '../../shared/account-expiry';
import { formatRelativeDate } from '../../shared/date-helper';
import { messages } from '../../shared/gettext';
import { useAppContext } from '../context';
import useActions from '../lib/actionsHook';
import { transitions, useHistory } from '../lib/history';
-import { RoutePath } from '../lib/routes';
+import { generateRoutePath, RoutePath } from '../lib/routes';
import account from '../redux/account/actions';
-import { IReduxState } from '../redux/store';
import * as AppButton from './AppButton';
import { AriaDescribed, AriaDescription, AriaDescriptionGroup } from './AriaGroup';
import { bigText } from './common-styles';
@@ -24,6 +24,7 @@ import {
RedeemVoucherResponse,
RedeemVoucherSubmitButton,
} from './RedeemVoucher';
+import { useParams } from 'react-router';
export const StyledHeader = styled(DefaultHeaderBar)({
flex: 0,
@@ -82,9 +83,13 @@ export const StyledStatusIcon = styled.div({
export function VoucherInput() {
const history = useHistory();
- const onSuccess = useCallback(() => {
- history.push(RoutePath.voucherSuccess);
- }, [history]);
+ const onSuccess = useCallback(
+ (newExpiry: string, secondsAdded: number) => {
+ const path = generateRoutePath(RoutePath.voucherSuccess, { newExpiry, secondsAdded });
+ history.push(path);
+ },
+ [history],
+ );
const navigateBack = useCallback(() => {
history.pop();
@@ -119,23 +124,31 @@ export function VoucherInput() {
}
export function VoucherVerificationSuccess() {
+ const { newExpiry, secondsAdded } = useParams<{ newExpiry: string; secondsAdded: string }>();
+
return (
- <TimeAdded title={messages.pgettext('connect-view', 'Voucher was successfully redeemed')} />
+ <TimeAdded
+ newExpiry={newExpiry}
+ secondsAdded={parseInt(secondsAdded)}
+ title={messages.pgettext('connect-view', 'Voucher was successfully redeemed')}
+ />
);
}
interface ITimeAddedProps {
title?: string;
+ newExpiry?: string;
+ secondsAdded?: number;
}
export function TimeAdded(props: ITimeAddedProps) {
const history = useHistory();
const finish = useFinishedCallback();
- const accountData = useSelector((state: IReduxState) => state.account);
+ const expiry = useSelector((state) => state.account.expiry);
const isNewAccount = useSelector(
- (state: IReduxState) =>
- state.account.status.type === 'ok' && state.account.status.method === 'new_account',
+ (state) => state.account.status.type === 'ok' && state.account.status.method === 'new_account',
);
+ const locale = useSelector((state) => state.userInterface.locale);
const navigateToSetupFinished = useCallback(() => {
if (isNewAccount) {
@@ -146,10 +159,8 @@ export function TimeAdded(props: ITimeAddedProps) {
}, [history, finish]);
const duration =
- (accountData.expiry &&
- accountData.previousExpiry &&
- formatRelativeDate(accountData.expiry, accountData.previousExpiry)) ??
- '';
+ props.secondsAdded !== undefined ? formatRelativeDate(props.secondsAdded * 1000, 0) : undefined;
+ const newExpiry = formatDate(props.newExpiry ?? expiry!, locale);
return (
<Layout>
@@ -164,7 +175,17 @@ export function TimeAdded(props: ITimeAddedProps) {
{props.title ?? messages.pgettext('connect-view', 'Time was successfully added')}
</StyledTitle>
<StyledLabel>
- {sprintf(messages.gettext('%(duration)s was added to your account.'), { duration })}
+ {duration
+ ? sprintf(
+ messages.gettext('%(duration)s was added, account paid until %(expiry)s.'),
+ {
+ duration,
+ expiry: newExpiry,
+ },
+ )
+ : sprintf(messages.gettext('Account paid until %(expiry)s.'), {
+ expiry: newExpiry,
+ })}
</StyledLabel>
</StyledBody>
@@ -238,10 +259,9 @@ export function SetupFinished() {
function HeaderBar() {
const isNewAccount = useSelector(
- (state: IReduxState) =>
- state.account.status.type === 'ok' && state.account.status.method === 'new_account',
+ (state) => state.account.status.type === 'ok' && state.account.status.method === 'new_account',
);
- const tunnelState = useSelector((state: IReduxState) => state.connection.status);
+ const tunnelState = useSelector((state) => state.connection.status);
const headerBarStyle = isNewAccount
? HeaderBarStyle.default
: calculateHeaderBarStyle(tunnelState);
@@ -254,8 +274,7 @@ function useFinishedCallback() {
const history = useHistory();
const isNewAccount = useSelector(
- (state: IReduxState) =>
- state.account.status.type === 'ok' && state.account.status.method === 'new_account',
+ (state) => state.account.status.type === 'ok' && state.account.status.method === 'new_account',
);
const callback = useCallback(() => {
diff --git a/gui/src/renderer/lib/history.tsx b/gui/src/renderer/lib/history.tsx
index 4abdcd5fcb..f5bc213112 100644
--- a/gui/src/renderer/lib/history.tsx
+++ b/gui/src/renderer/lib/history.tsx
@@ -1,7 +1,7 @@
import { Location, Action, LocationDescriptorObject, History as OriginalHistory } from 'history';
import React from 'react';
import { RouteComponentProps, useHistory as useReactRouterHistory, withRouter } from 'react-router';
-import { RoutePath } from './routes';
+import { GeneratedRoutePath, RoutePath } from './routes';
export interface ITransitionSpecification {
name: string;
@@ -38,7 +38,7 @@ export const transitions: ITransitionMap = {
},
};
-type LocationDescriptor<S> = RoutePath | LocationDescriptorObject<S>;
+type LocationDescriptor<S> = RoutePath | GeneratedRoutePath | LocationDescriptorObject<S>;
type LocationListener<S = unknown> = (
location: Location<S>,
@@ -168,7 +168,11 @@ export default class History {
}
private createLocation(location: LocationDescriptor<S>, state?: S): Location<S> {
- if (typeof location === 'object') {
+ if (typeof location === 'string') {
+ return this.createLocationFromString(location, state);
+ } else if ('routePath' in location) {
+ return this.createLocationFromString(location.routePath, state);
+ } else {
return {
pathname: location.pathname ?? this.location.pathname,
search: location.search ?? '',
@@ -176,17 +180,19 @@ export default class History {
state: location.state,
key: location.key ?? this.getRandomKey(),
};
- } else {
- return {
- pathname: location,
- search: '',
- hash: '',
- state,
- key: this.getRandomKey(),
- };
}
}
+ private createLocationFromString(path: string, state?: S): Location<S> {
+ return {
+ pathname: path,
+ search: '',
+ hash: '',
+ state,
+ key: this.getRandomKey(),
+ };
+ }
+
private getRandomKey() {
return Math.random().toString(36).substr(8);
}
diff --git a/gui/src/renderer/lib/routes.ts b/gui/src/renderer/lib/routes.ts
index dee83c2a42..8a63cd6de0 100644
--- a/gui/src/renderer/lib/routes.ts
+++ b/gui/src/renderer/lib/routes.ts
@@ -1,9 +1,13 @@
+import { generatePath } from 'react-router';
+
+export type GeneratedRoutePath = { routePath: string };
+
export enum RoutePath {
launch = '/',
login = '/login',
main = '/main',
redeemVoucher = '/main/voucher/redeem',
- voucherSuccess = '/main/voucher/success',
+ voucherSuccess = '/main/voucher/success/:newExpiry/:secondsAdded',
timeAdded = '/main/time-added',
setupFinished = '/main/setup-finished',
settings = '/settings',
@@ -19,3 +23,10 @@ export enum RoutePath {
selectLocation = '/select-location',
filterByProvider = '/select-location/filter-by-provider',
}
+
+export function generateRoutePath(
+ routePath: RoutePath,
+ parameters: Parameters<typeof generatePath>[1],
+): GeneratedRoutePath {
+ return { routePath: generatePath(routePath, parameters) };
+}