summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-04-08 18:29:27 +0200
committerOskar Nyberg <oskar@mullvad.net>2021-04-14 13:44:18 +0200
commiteee8fa46952a440d1cb5c86e5a8e9e2f53692f9b (patch)
tree62293f07a8308a630dbfefb29ccd7cbe816a2930
parent24deeb35afcda1bc4083ec0c47c4928dc4249b03 (diff)
downloadmullvadvpn-eee8fa46952a440d1cb5c86e5a8e9e2f53692f9b.tar.xz
mullvadvpn-eee8fa46952a440d1cb5c86e5a8e9e2f53692f9b.zip
Switch to native APIs and own implementation from moment.js
-rw-r--r--gui/src/main/account-data-cache.ts11
-rw-r--r--gui/src/main/index.ts3
-rw-r--r--gui/src/renderer/components/Settings.tsx3
-rw-r--r--gui/src/renderer/components/WireguardKeys.tsx16
-rw-r--r--gui/src/renderer/containers/SettingsPage.tsx1
-rw-r--r--gui/src/renderer/containers/WireguardKeysPage.tsx1
-rw-r--r--gui/src/shared/account-expiry.ts53
-rw-r--r--gui/src/shared/logging.ts17
-rw-r--r--gui/src/shared/notifications/close-to-account-expiry.ts16
9 files changed, 49 insertions, 72 deletions
diff --git a/gui/src/main/account-data-cache.ts b/gui/src/main/account-data-cache.ts
index a03728e004..7d3c59e5ef 100644
--- a/gui/src/main/account-data-cache.ts
+++ b/gui/src/main/account-data-cache.ts
@@ -1,6 +1,6 @@
-import moment from 'moment';
-import { hasExpired } from '../shared/account-expiry';
+import { closeToExpiry, hasExpired } from '../shared/account-expiry';
import { AccountToken, IAccountData } from '../shared/daemon-rpc-types';
+import { DateComponent, dateByAddingComponent } from '../shared/date-helper';
import log from '../shared/logging';
import consumePromise from '../shared/promise';
import { Scheduler } from '../shared/scheduler';
@@ -105,13 +105,12 @@ export default class AccountDataCache {
private calculateRefetchDelay(accountExpiry: string) {
const currentDate = new Date();
- const oneMinuteBeforeExpiry = moment(accountExpiry).subtract(1, 'minute');
- const closeToExpiry = moment(accountExpiry).isSameOrBefore(moment().add(3, 'days'));
+ const oneMinuteBeforeExpiry = dateByAddingComponent(accountExpiry, DateComponent.minute, -1);
if (hasExpired(accountExpiry)) {
return EXPIRED_ACCOUNT_REFRESH_PERIOD;
- } else if (oneMinuteBeforeExpiry.isSameOrAfter(currentDate) && closeToExpiry) {
- return oneMinuteBeforeExpiry.diff(currentDate);
+ } else if (oneMinuteBeforeExpiry >= currentDate && closeToExpiry(accountExpiry)) {
+ return oneMinuteBeforeExpiry.getTime() - currentDate.getTime();
} else {
return undefined;
}
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index efbf9a4aa3..43807de5c3 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -10,7 +10,6 @@ import {
shell,
Tray,
} from 'electron';
-import moment from 'moment';
import * as path from 'path';
import { sprintf } from 'sprintf-js';
import * as uuid from 'uuid';
@@ -1339,7 +1338,7 @@ class ApplicationMain {
this.notificationController.notify(closeToExpiryNotification.getSystemNotification());
const twelveHours = 12 * 60 * 60 * 1000;
- const remainingMilliseconds = moment(this.accountData.expiry).diff(new Date());
+ const remainingMilliseconds = new Date(this.accountData.expiry).getTime() - Date.now();
const delay = Math.min(twelveHours, remainingMilliseconds);
this.accountExpiryNotificationScheduler.schedule(() => this.handleAccountExpiry(), delay);
}
diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx
index 00c5d846f0..56b8fd5494 100644
--- a/gui/src/renderer/components/Settings.tsx
+++ b/gui/src/renderer/components/Settings.tsx
@@ -29,7 +29,6 @@ export interface IProps {
preferredLocaleDisplayName: string;
loginState: LoginState;
accountExpiry?: string;
- expiryLocale: string;
appVersion: string;
consistentVersion: boolean;
upToDateVersion: boolean;
@@ -106,7 +105,7 @@ export default class Settings extends React.Component<IProps> {
const isOutOfTime = this.props.accountExpiry ? hasExpired(this.props.accountExpiry) : false;
const formattedExpiry = this.props.accountExpiry
- ? formatRemainingTime(this.props.accountExpiry, this.props.expiryLocale).toUpperCase()
+ ? formatRemainingTime(this.props.accountExpiry).toUpperCase()
: '';
const outOfTimeMessage = messages.pgettext('settings-view', 'OUT OF TIME');
diff --git a/gui/src/renderer/components/WireguardKeys.tsx b/gui/src/renderer/components/WireguardKeys.tsx
index 9c181ad57e..f6e143e6a3 100644
--- a/gui/src/renderer/components/WireguardKeys.tsx
+++ b/gui/src/renderer/components/WireguardKeys.tsx
@@ -1,7 +1,7 @@
-import moment from 'moment';
import * as React from 'react';
import { sprintf } from 'sprintf-js';
import { TunnelState } from '../../shared/daemon-rpc-types';
+import { formatRelativeDate } from '../../shared/date-helper';
import { messages } from '../../shared/gettext';
import log from '../../shared/logging';
import { IWgKey, WgKeyState } from '../redux/settings/reducers';
@@ -35,7 +35,6 @@ import {
export interface IProps {
keyState: WgKeyState;
isOffline: boolean;
- locale: string;
tunnelState: TunnelState;
windowFocused: boolean;
@@ -56,14 +55,14 @@ export default class WireguardKeys extends React.Component<IProps, IState> {
public state = {
recentlyGeneratedKey: false,
userHasInitiatedVerification: false,
- ageOfKeyString: WireguardKeys.ageOfKeyString(this.props.keyState, this.props.locale),
+ ageOfKeyString: WireguardKeys.ageOfKeyString(this.props.keyState),
};
private keyAgeUpdateInterval?: number;
public static getDerivedStateFromProps(props: IProps) {
return {
- ageOfKeyString: WireguardKeys.ageOfKeyString(props.keyState, props.locale),
+ ageOfKeyString: WireguardKeys.ageOfKeyString(props.keyState),
};
}
@@ -276,11 +275,12 @@ export default class WireguardKeys extends React.Component<IProps, IState> {
}
}
- private static ageOfKeyString(keyState: WgKeyState, locale: string): string {
+ private static ageOfKeyString(keyState: WgKeyState): string {
switch (keyState.type) {
case 'key-set':
- case 'being-verified':
- return moment(keyState.key.created).locale(locale).fromNow();
+ case 'being-verified': {
+ return formatRelativeDate(new Date(), keyState.key.created, true);
+ }
default:
return '-';
}
@@ -288,7 +288,7 @@ export default class WireguardKeys extends React.Component<IProps, IState> {
private setAgeOfKeyStringState = () => {
this.setState({
- ageOfKeyString: WireguardKeys.ageOfKeyString(this.props.keyState, this.props.locale),
+ ageOfKeyString: WireguardKeys.ageOfKeyString(this.props.keyState),
});
};
diff --git a/gui/src/renderer/containers/SettingsPage.tsx b/gui/src/renderer/containers/SettingsPage.tsx
index 7d233e9f06..6985fd927c 100644
--- a/gui/src/renderer/containers/SettingsPage.tsx
+++ b/gui/src/renderer/containers/SettingsPage.tsx
@@ -10,7 +10,6 @@ const mapStateToProps = (state: IReduxState, props: IAppContext) => ({
),
loginState: state.account.status,
accountExpiry: state.account.expiry,
- expiryLocale: state.userInterface.locale,
appVersion: state.version.current,
consistentVersion: state.version.consistent,
upToDateVersion: state.version.suggestedUpgrade ? false : true,
diff --git a/gui/src/renderer/containers/WireguardKeysPage.tsx b/gui/src/renderer/containers/WireguardKeysPage.tsx
index 9e5fa6fc8c..e07e4718f2 100644
--- a/gui/src/renderer/containers/WireguardKeysPage.tsx
+++ b/gui/src/renderer/containers/WireguardKeysPage.tsx
@@ -9,7 +9,6 @@ import { IReduxState, ReduxDispatch } from '../redux/store';
const mapStateToProps = (state: IReduxState) => ({
keyState: state.settings.wireguardKeyState,
isOffline: state.connection.isBlocked,
- locale: state.userInterface.locale,
tunnelState: state.connection.status,
windowFocused: state.userInterface.windowFocused,
});
diff --git a/gui/src/shared/account-expiry.ts b/gui/src/shared/account-expiry.ts
index a76eb6ecc4..7ca382efb2 100644
--- a/gui/src/shared/account-expiry.ts
+++ b/gui/src/shared/account-expiry.ts
@@ -1,52 +1,27 @@
-import moment from 'moment';
-import { sprintf } from 'sprintf-js';
-import { messages } from './gettext';
+import { DateComponent, DateType, formatRelativeDate, dateByAddingComponent } from './date-helper';
import { capitalize } from './string-helpers';
-type DateArgument = string | Date | moment.Moment;
-
-export function hasExpired(expiry: DateArgument): boolean {
- return moment(expiry).isSameOrBefore(new Date());
+export function hasExpired(expiry: DateType): boolean {
+ return new Date(expiry).getTime() < Date.now();
}
-export function formatDate(date: DateArgument, locale: string): string {
- return moment(date).locale(locale).format('lll');
+export function closeToExpiry(expiry: DateType): boolean {
+ return (
+ !hasExpired(expiry) &&
+ new Date(expiry) <= dateByAddingComponent(new Date(), DateComponent.day, 3)
+ );
}
-export function formatDurationUntilExpiry(expiry: DateArgument, locale: string): string {
- const expiryMoment = moment(expiry).locale(locale);
- const daysDiff = expiryMoment.diff(new Date(), 'days');
-
- // Below three months we want to show the duration in days. Moments fromNow() method starts
- // measuring duration in months from 26 days and up.
- // https://momentjs.com/docs/#/displaying/fromnow/
- if (daysDiff >= 26 && daysDiff <= 90) {
- return sprintf(
- // TRANSLATORS: The remaining time left on the account measured in days.
- // TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(duration)s - The remaining time measured in days.
- messages.pgettext('account-expiry', '%(duration)s days'),
- { duration: daysDiff },
- );
- } else {
- return expiryMoment.fromNow(true);
- }
+export function formatDate(date: DateType, locale: string): string {
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }).format(
+ new Date(date),
+ );
}
export function formatRemainingTime(
- expiry: DateArgument,
- locale: string,
+ expiry: DateType,
shouldCapitalizeFirstLetter?: boolean,
): string {
- const duration = formatDurationUntilExpiry(expiry, locale);
-
- const remaining = sprintf(
- // TRANSLATORS: The remaining time left on the account displayed across the app.
- // TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(duration)s - a localized remaining time (in minutes, hours, or days) until the account expiry
- messages.pgettext('account-expiry', '%(duration)s left'),
- { duration },
- );
-
+ const remaining = formatRelativeDate(new Date(), expiry, true);
return shouldCapitalizeFirstLetter ? capitalize(remaining) : remaining;
}
diff --git a/gui/src/shared/logging.ts b/gui/src/shared/logging.ts
index 84eaa5f25c..f8c71fc41e 100644
--- a/gui/src/shared/logging.ts
+++ b/gui/src/shared/logging.ts
@@ -1,4 +1,3 @@
-import moment from 'moment';
import { ILogInput, ILogOutput, LogLevel } from './logging-types';
export class Logger {
@@ -13,7 +12,7 @@ export class Logger {
}
public log(level: LogLevel, ...data: unknown[]) {
- const time = moment().format('YYYY-MM-DD HH:mm:ss.SSS');
+ const time = this.getDateString();
const stringifiedData = data.map(this.stringifyData).join(' ');
const message = `[${time}][${LogLevel[level]}] ${stringifiedData}`;
@@ -30,6 +29,20 @@ export class Logger {
this.outputs.forEach((output) => output.dispose?.());
}
+ private getDateString(): string {
+ const date = new Date();
+ const year = date.getFullYear();
+ const month = Number(date.getMonth() + 1)
+ .toString()
+ .padStart(2, '0');
+ const day = Number(date.getDate()).toString().padStart(2, '0');
+ const hour = Number(date.getHours()).toString().padStart(2, '0');
+ const minute = Number(date.getMinutes()).toString().padStart(2, '0');
+ const second = Number(date.getSeconds()).toString().padStart(2, '0');
+ const millisecond = Number(date.getMilliseconds()).toString().padStart(3, '0');
+ return `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}`;
+ }
+
private stringifyData(data: unknown): string {
return typeof data === 'string' ? data : JSON.stringify(data);
}
diff --git a/gui/src/shared/notifications/close-to-account-expiry.ts b/gui/src/shared/notifications/close-to-account-expiry.ts
index 2e3c5ae49e..e0f13b33b3 100644
--- a/gui/src/shared/notifications/close-to-account-expiry.ts
+++ b/gui/src/shared/notifications/close-to-account-expiry.ts
@@ -1,8 +1,8 @@
-import moment from 'moment';
import { sprintf } from 'sprintf-js';
import { links } from '../../config.json';
import { messages } from '../../shared/gettext';
-import { formatDurationUntilExpiry, formatRemainingTime, hasExpired } from '../account-expiry';
+import { closeToExpiry, formatRemainingTime } from '../account-expiry';
+import { formatRelativeDate } from '../date-helper';
import {
InAppNotification,
InAppNotificationProvider,
@@ -19,13 +19,7 @@ export class CloseToAccountExpiryNotificationProvider
implements InAppNotificationProvider, SystemNotificationProvider {
public constructor(private context: CloseToAccountExpiryNotificationContext) {}
- public mayDisplay() {
- const willHaveExpiredInThreeDays = moment(this.context.accountExpiry).isSameOrBefore(
- moment().add(3, 'days'),
- );
-
- return !hasExpired(this.context.accountExpiry) && willHaveExpiredInThreeDays;
- }
+ public mayDisplay = () => closeToExpiry(this.context.accountExpiry);
public getSystemNotification(): SystemNotification {
const message = sprintf(
@@ -37,7 +31,7 @@ export class CloseToAccountExpiryNotificationProvider
'Account credit expires in %(duration)s. Buy more credit.',
),
{
- duration: formatDurationUntilExpiry(this.context.accountExpiry, this.context.locale),
+ duration: formatRelativeDate(new Date(), this.context.accountExpiry),
},
);
@@ -56,7 +50,7 @@ export class CloseToAccountExpiryNotificationProvider
public getInAppNotification(): InAppNotification {
const subtitle = sprintf(
messages.pgettext('in-app-notifications', '%(duration)s. Buy more credit.'),
- { duration: formatRemainingTime(this.context.accountExpiry, this.context.locale, true) },
+ { duration: formatRemainingTime(this.context.accountExpiry, true) },
);
return {