diff options
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/main/account-data-cache.ts | 35 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 15 | ||||
| -rw-r--r-- | gui/src/renderer/app.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/Account.tsx | 5 | ||||
| -rw-r--r-- | gui/src/renderer/components/Settings.tsx | 9 | ||||
| -rw-r--r-- | gui/src/renderer/containers/AccountPage.tsx | 1 | ||||
| -rw-r--r-- | gui/src/renderer/containers/SettingsPage.tsx | 1 | ||||
| -rw-r--r-- | gui/src/shared/account-expiry.ts | 4 | ||||
| -rw-r--r-- | gui/src/shared/ipc-schema.ts | 1 |
9 files changed, 56 insertions, 19 deletions
diff --git a/gui/src/main/account-data-cache.ts b/gui/src/main/account-data-cache.ts index 8b81a29472..a79a934537 100644 --- a/gui/src/main/account-data-cache.ts +++ b/gui/src/main/account-data-cache.ts @@ -6,18 +6,21 @@ import consumePromise from '../shared/promise'; import { Scheduler } from '../shared/scheduler'; import { InvalidAccountError } from './errors'; -const EXPIRED_ACCOUNT_REFRESH_PERIOD = 60_000; - interface IAccountFetchWatcher { onFinish: () => void; onError: (error: Error) => void; } +// Account data is valid for 1 minute unless the account has expired. +const ACCOUNT_DATA_VALIDITY_SECONDS = 60_000; +// Account data is valid for 10 seconds if the account has expired. +const ACCOUNT_DATA_EXPIRED_VALIDITY_SECONDS = 10_000; + // An account data cache that helps to throttle RPC requests to get_account_data and retain the // cached value for 1 minute. export default class AccountDataCache { private currentAccount?: AccountToken; - private expiresAt?: Date; + private validUntil?: Date; private performingFetch = false; private waitStrategy = new WaitStrategy(); private fetchRetryScheduler = new Scheduler(); @@ -36,7 +39,7 @@ export default class AccountDataCache { } // Only fetch if value has expired - if (this.isExpired()) { + if (!this.isValid()) { if (watcher) { this.watchers.push(watcher); } @@ -59,7 +62,7 @@ export default class AccountDataCache { this.waitStrategy.reset(); this.performingFetch = false; - this.expiresAt = undefined; + this.validUntil = undefined; this.updateHandler(); this.notifyWatchers((watcher) => { watcher.onError(new Error('Cancelled')); @@ -72,14 +75,22 @@ export default class AccountDataCache { } } - private setValue(value: IAccountData) { - this.expiresAt = new Date(Date.now() + 60 * 1000); // 60s expiration - this.updateHandler(value); + private setValue(accountData: IAccountData) { + this.validUntil = this.getValidUntil(accountData); + this.updateHandler(accountData); this.notifyWatchers((watcher) => watcher.onFinish()); } - private isExpired() { - return !this.expiresAt || this.expiresAt < new Date(); + private isValid() { + return this.validUntil && this.validUntil > new Date(); + } + + private getValidUntil(accountData: IAccountData): Date { + if (hasExpired(accountData.expiry)) { + return new Date(Date.now() + ACCOUNT_DATA_EXPIRED_VALIDITY_SECONDS); + } else { + return new Date(Date.now() + ACCOUNT_DATA_VALIDITY_SECONDS); + } } private async performFetch(accountToken: AccountToken) { @@ -113,9 +124,7 @@ export default class AccountDataCache { const currentDate = new Date(); const oneMinuteBeforeExpiry = dateByAddingComponent(accountExpiry, DateComponent.minute, -1); - if (hasExpired(accountExpiry)) { - return EXPIRED_ACCOUNT_REFRESH_PERIOD; - } else if (oneMinuteBeforeExpiry >= currentDate && closeToExpiry(accountExpiry)) { + 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 a139434020..8b047cb6c3 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -15,7 +15,7 @@ import * as path from 'path'; import { sprintf } from 'sprintf-js'; import * as uuid from 'uuid'; import config from '../config.json'; -import { hasExpired } from '../shared/account-expiry'; +import { closeToExpiry, hasExpired } from '../shared/account-expiry'; import { IApplication } from '../shared/application-types'; import BridgeSettingsBuilder from '../shared/bridge-settings-builder'; import { @@ -1049,14 +1049,20 @@ class ApplicationMain { } private registerWindowListener(windowController: WindowController) { - windowController.window?.on('show', () => { + windowController.window?.on('focus', () => { // cancel notifications when window appears this.notificationController.cancelPendingNotifications(); - this.updateAccountData(); + if ( + !this.accountData || + closeToExpiry(this.accountData.expiry, 4) || + hasExpired(this.accountData.expiry) + ) { + this.updateAccountData(); + } }); - windowController.window?.on('hide', () => { + windowController.window?.on('blur', () => { // ensure notification guard is reset this.notificationController.resetTunnelStateAnnouncements(); }); @@ -1172,6 +1178,7 @@ class ApplicationMain { return response; }); + IpcMainEventChannel.account.handleUpdateData(() => this.updateAccountData()); IpcMainEventChannel.accountHistory.handleClear(async () => { await this.daemonRpc.clearAccountHistory(); diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index 677286c092..febcaf8bc8 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -318,6 +318,10 @@ export default class AppRenderer { return IpcRendererEventChannel.account.submitVoucher(voucherCode); } + public updateAccountData(): void { + IpcRendererEventChannel.account.updateData(); + } + public async connectTunnel(): Promise<void> { const state = this.tunnelState.state; diff --git a/gui/src/renderer/components/Account.tsx b/gui/src/renderer/components/Account.tsx index 18980ca1a9..02e2b9f081 100644 --- a/gui/src/renderer/components/Account.tsx +++ b/gui/src/renderer/components/Account.tsx @@ -31,9 +31,14 @@ interface IProps { onLogout: () => void; onClose: () => void; onBuyMore: () => Promise<void>; + updateAccountData: () => void; } export default class Account extends React.Component<IProps> { + public componentDidMount() { + this.props.updateAccountData(); + } + public render() { return ( <ModalContainer> diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx index 56b8fd5494..1610f776f9 100644 --- a/gui/src/renderer/components/Settings.tsx +++ b/gui/src/renderer/components/Settings.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { colors, links } from '../../config.json'; import { hasExpired, formatRemainingTime } from '../../shared/account-expiry'; import { messages } from '../../shared/gettext'; +import History from '../lib/history'; import { AriaDescribed, AriaDescription, AriaDescriptionGroup } from './AriaGroup'; import * as Cell from './cell'; import { Layout } from './Layout'; @@ -42,9 +43,17 @@ export interface IProps { onViewPreferences: () => void; onViewAdvancedSettings: () => void; onExternalLink: (url: string) => void; + updateAccountData: () => void; + history: History; } export default class Settings extends React.Component<IProps> { + public componentDidMount() { + if (this.props.history.action === 'PUSH') { + this.props.updateAccountData(); + } + } + public render() { const showLargeTitle = this.props.loginState.type !== 'ok'; diff --git a/gui/src/renderer/containers/AccountPage.tsx b/gui/src/renderer/containers/AccountPage.tsx index 985161c838..8751107c74 100644 --- a/gui/src/renderer/containers/AccountPage.tsx +++ b/gui/src/renderer/containers/AccountPage.tsx @@ -22,6 +22,7 @@ const mapDispatchToProps = (_dispatch: ReduxDispatch, props: IHistoryProps & IAp props.history.pop(); }, onBuyMore: () => props.app.openLinkWithAuth(links.purchase), + updateAccountData: () => props.app.updateAccountData(), }; }; diff --git a/gui/src/renderer/containers/SettingsPage.tsx b/gui/src/renderer/containers/SettingsPage.tsx index 97ff917e7e..57ded8cd24 100644 --- a/gui/src/renderer/containers/SettingsPage.tsx +++ b/gui/src/renderer/containers/SettingsPage.tsx @@ -26,6 +26,7 @@ const mapDispatchToProps = (_dispatch: ReduxDispatch, props: IHistoryProps & IAp onViewPreferences: () => props.history.push('/settings/preferences'), onViewAdvancedSettings: () => props.history.push('/settings/advanced'), onExternalLink: (url: string) => props.app.openUrl(url), + updateAccountData: () => props.app.updateAccountData(), }; }; diff --git a/gui/src/shared/account-expiry.ts b/gui/src/shared/account-expiry.ts index 7ca382efb2..81c45fec07 100644 --- a/gui/src/shared/account-expiry.ts +++ b/gui/src/shared/account-expiry.ts @@ -5,10 +5,10 @@ export function hasExpired(expiry: DateType): boolean { return new Date(expiry).getTime() < Date.now(); } -export function closeToExpiry(expiry: DateType): boolean { +export function closeToExpiry(expiry: DateType, days = 3): boolean { return ( !hasExpired(expiry) && - new Date(expiry) <= dateByAddingComponent(new Date(), DateComponent.day, 3) + new Date(expiry) <= dateByAddingComponent(new Date(), DateComponent.day, days) ); } diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts index 5789bcc149..e2b3ebbed3 100644 --- a/gui/src/shared/ipc-schema.ts +++ b/gui/src/shared/ipc-schema.ts @@ -164,6 +164,7 @@ export const ipcSchema = { logout: invoke<void, void>(), getWwwAuthToken: invoke<void, string>(), submitVoucher: invoke<string, VoucherResponse>(), + updateData: send<void>(), }, accountHistory: { '': notifyRenderer<AccountToken | undefined>(), |
