diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2021-04-08 18:29:27 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2021-04-14 13:44:18 +0200 |
| commit | eee8fa46952a440d1cb5c86e5a8e9e2f53692f9b (patch) | |
| tree | 62293f07a8308a630dbfefb29ccd7cbe816a2930 | |
| parent | 24deeb35afcda1bc4083ec0c47c4928dc4249b03 (diff) | |
| download | mullvadvpn-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.ts | 11 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/Settings.tsx | 3 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardKeys.tsx | 16 | ||||
| -rw-r--r-- | gui/src/renderer/containers/SettingsPage.tsx | 1 | ||||
| -rw-r--r-- | gui/src/renderer/containers/WireguardKeysPage.tsx | 1 | ||||
| -rw-r--r-- | gui/src/shared/account-expiry.ts | 53 | ||||
| -rw-r--r-- | gui/src/shared/logging.ts | 17 | ||||
| -rw-r--r-- | gui/src/shared/notifications/close-to-account-expiry.ts | 16 |
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 { |
