Browse Source

Reduces validation checks

main
Eric Amodio 1 year ago
parent
commit
4dd40cc045
4 changed files with 76 additions and 37 deletions
  1. +1
    -1
      src/constants.ts
  2. +60
    -26
      src/plus/subscription/subscriptionService.ts
  3. +13
    -10
      src/plus/webviews/account/accountWebview.ts
  4. +2
    -0
      src/subscription.ts

+ 1
- 1
src/constants.ts View File

@ -748,7 +748,7 @@ export type GlobalStorage = {
pendingWelcomeOnFocus: boolean; pendingWelcomeOnFocus: boolean;
pendingWhatsNewOnFocus: boolean; pendingWhatsNewOnFocus: boolean;
// Don't change this key name ('premium`) as its the stored subscription // Don't change this key name ('premium`) as its the stored subscription
'premium:subscription': Stored<Subscription>;
'premium:subscription': Stored<Subscription & { lastValidatedAt: number | undefined }>;
'synced:version': string; 'synced:version': string;
// Keep the pre-release version separate from the released version // Keep the pre-release version separate from the released version
'synced:preVersion': string; 'synced:preVersion': string;

+ 60
- 26
src/plus/subscription/subscriptionService.ts View File

@ -99,7 +99,7 @@ export class SubscriptionService implements Disposable {
subscription.previewTrial = undefined; subscription.previewTrial = undefined;
} }
this.changeSubscription(subscription, true);
this.changeSubscription(subscription, { silent: true });
setTimeout(() => void this.ensureSession(false), 10000); setTimeout(() => void this.ensureSession(false), 10000);
} }
@ -130,7 +130,7 @@ export class SubscriptionService implements Disposable {
} }
this._session = session; this._session = session;
void this.validate();
void this.validate({ force: true });
} }
private _etag: number = 0; private _etag: number = 0;
@ -163,7 +163,7 @@ export class SubscriptionService implements Disposable {
registerCommand(Commands.PlusPurchase, () => this.purchase()), registerCommand(Commands.PlusPurchase, () => this.purchase()),
registerCommand(Commands.PlusResendVerification, () => this.resendVerification()), registerCommand(Commands.PlusResendVerification, () => this.resendVerification()),
registerCommand(Commands.PlusValidate, () => this.validate()),
registerCommand(Commands.PlusValidate, () => this.validate({ force: true })),
registerCommand(Commands.PlusShowPlans, () => this.showPlans()), registerCommand(Commands.PlusShowPlans, () => this.showPlans()),
@ -382,7 +382,7 @@ export class SubscriptionService implements Disposable {
); );
if (result === confirm) { if (result === confirm) {
await this.validate();
await this.validate({ force: true });
return true; return true;
} }
} catch (ex) { } catch (ex) {
@ -488,7 +488,7 @@ export class SubscriptionService implements Disposable {
@gate() @gate()
@log() @log()
async validate(): Promise<void> {
async validate(options?: { force?: boolean }): Promise<void> {
const scope = getLogScope(); const scope = getLogScope();
const session = await this.ensureSession(false); const session = await this.ensureSession(false);
@ -498,17 +498,30 @@ export class SubscriptionService implements Disposable {
} }
try { try {
await this.checkInAndValidate(session);
await this.checkInAndValidate(session, options);
} catch (ex) { } catch (ex) {
Logger.error(ex, scope); Logger.error(ex, scope);
debugger; debugger;
} }
} }
private _lastCheckInDate: Date | undefined;
private _lastValidatedDate: Date | undefined;
@gate<SubscriptionService['checkInAndValidate']>(s => s.account.id) @gate<SubscriptionService['checkInAndValidate']>(s => s.account.id)
private async checkInAndValidate(session: AuthenticationSession, showSlowProgress: boolean = false): Promise<void> {
if (!showSlowProgress) return this.checkInAndValidateCore(session);
private async checkInAndValidate(
session: AuthenticationSession,
options?: { force?: boolean; showSlowProgress?: boolean },
): Promise<void> {
// Only check in if we haven't in the last 12 hours
if (
!options?.force &&
this._lastValidatedDate != null &&
Date.now() - this._lastValidatedDate.getTime() < 12 * 60 * 60 * 1000 &&
!isSubscriptionExpired(this._subscription)
) {
return;
}
if (!options?.showSlowProgress) return this.checkInAndValidateCore(session);
const validating = this.checkInAndValidateCore(session); const validating = this.checkInAndValidateCore(session);
const result = await Promise.race([ const result = await Promise.race([
@ -527,7 +540,7 @@ export class SubscriptionService implements Disposable {
} }
} }
@debug<SubscriptionService['checkInAndValidate']>({ args: { 0: s => s?.account.label } })
@debug<SubscriptionService['checkInAndValidateCore']>({ args: { 0: s => s?.account.label } })
private async checkInAndValidateCore(session: AuthenticationSession): Promise<void> { private async checkInAndValidateCore(session: AuthenticationSession): Promise<void> {
const scope = getLogScope(); const scope = getLogScope();
@ -560,8 +573,8 @@ export class SubscriptionService implements Disposable {
const data: GKLicenseInfo = await rsp.json(); const data: GKLicenseInfo = await rsp.json();
this.validateSubscription(data); this.validateSubscription(data);
this._lastCheckInDate = new Date();
} catch (ex) { } catch (ex) {
this._lastValidatedDate = undefined;
Logger.error(ex, scope); Logger.error(ex, scope);
debugger; debugger;
if (ex instanceof AccountValidationError) throw ex; if (ex instanceof AccountValidationError) throw ex;
@ -580,11 +593,11 @@ export class SubscriptionService implements Disposable {
// Check 4 times a day to ensure we validate at least once a day // Check 4 times a day to ensure we validate at least once a day
this._validationTimer = setInterval( this._validationTimer = setInterval(
() => { () => {
if (this._lastCheckInDate == null || this._lastCheckInDate.getDate() !== new Date().getDate()) {
if (this._lastValidatedDate == null || this._lastValidatedDate.getDate() !== new Date().getDate()) {
void this.ensureSession(false, true); void this.ensureSession(false, true);
} }
}, },
1000 * 60 * 60 * 6,
6 * 60 * 60 * 1000,
); );
} }
@ -665,14 +678,18 @@ export class SubscriptionService implements Disposable {
effective = { ...actual }; effective = { ...actual };
} }
this.changeSubscription({
...this._subscription,
plan: {
actual: actual,
effective: effective,
this._lastValidatedDate = new Date();
this.changeSubscription(
{
...this._subscription,
plan: {
actual: actual,
effective: effective,
},
account: account,
}, },
account: account,
});
{ store: true },
);
} }
private _sessionPromise: Promise<AuthenticationSession | null> | undefined; private _sessionPromise: Promise<AuthenticationSession | null> | undefined;
@ -681,6 +698,10 @@ export class SubscriptionService implements Disposable {
@gate() @gate()
@debug() @debug()
private async ensureSession(createIfNeeded: boolean, force?: boolean): Promise<AuthenticationSession | undefined> { private async ensureSession(createIfNeeded: boolean, force?: boolean): Promise<AuthenticationSession | undefined> {
if (force) {
this._lastValidatedDate = undefined;
}
if (this._sessionPromise != null && this._session === undefined) { if (this._sessionPromise != null && this._session === undefined) {
void (await this._sessionPromise); void (await this._sessionPromise);
} }
@ -737,7 +758,7 @@ export class SubscriptionService implements Disposable {
} }
try { try {
await this.checkInAndValidate(session, createIfNeeded);
await this.checkInAndValidate(session, { showSlowProgress: createIfNeeded });
} catch (ex) { } catch (ex) {
Logger.error(ex, scope); Logger.error(ex, scope);
debugger; debugger;
@ -802,7 +823,7 @@ export class SubscriptionService implements Disposable {
@debug() @debug()
private changeSubscription( private changeSubscription(
subscription: Optional<Subscription, 'state'> | undefined, subscription: Optional<Subscription, 'state'> | undefined,
silent: boolean = false,
options?: { silent?: boolean; store?: boolean },
): void { ): void {
if (subscription == null) { if (subscription == null) {
subscription = { subscription = {
@ -855,7 +876,12 @@ export class SubscriptionService implements Disposable {
const matches = previous != null && JSON.stringify(previous) === JSON.stringify(subscription); const matches = previous != null && JSON.stringify(previous) === JSON.stringify(subscription);
// If the previous and new subscriptions are exactly the same, kick out // If the previous and new subscriptions are exactly the same, kick out
if (matches) return;
if (matches) {
if (options?.store) {
void this.storeSubscription(subscription);
}
return;
}
queueMicrotask(() => { queueMicrotask(() => {
let data = flattenSubscription(subscription); let data = flattenSubscription(subscription);
@ -874,7 +900,7 @@ export class SubscriptionService implements Disposable {
this._subscription = subscription; this._subscription = subscription;
this._etag = Date.now(); this._etag = Date.now();
if (!silent) {
if (!options?.silent) {
this.updateContext(); this.updateContext();
if (previous != null) { if (previous != null) {
@ -886,7 +912,15 @@ export class SubscriptionService implements Disposable {
private getStoredSubscription(): Subscription | undefined { private getStoredSubscription(): Subscription | undefined {
const storedSubscription = this.container.storage.get('premium:subscription'); const storedSubscription = this.container.storage.get('premium:subscription');
const subscription = storedSubscription?.data;
let lastValidatedAt: number | undefined;
let subscription: Subscription | undefined;
if (storedSubscription?.data != null) {
({ lastValidatedAt, ...subscription } = storedSubscription.data);
this._lastValidatedDate = lastValidatedAt != null ? new Date(lastValidatedAt) : undefined;
} else {
subscription = undefined;
}
if (subscription != null) { if (subscription != null) {
// Migrate the plan names to the latest names // Migrate the plan names to the latest names
(subscription.plan.actual as Mutable<Subscription['plan']['actual']>).name = getSubscriptionPlanName( (subscription.plan.actual as Mutable<Subscription['plan']['actual']>).name = getSubscriptionPlanName(
@ -903,7 +937,7 @@ export class SubscriptionService implements Disposable {
private async storeSubscription(subscription: Subscription): Promise<void> { private async storeSubscription(subscription: Subscription): Promise<void> {
return this.container.storage.store('premium:subscription', { return this.container.storage.store('premium:subscription', {
v: 1, v: 1,
data: subscription,
data: { ...subscription, lastValidatedAt: this._lastValidatedDate?.getTime() },
}); });
} }

+ 13
- 10
src/plus/webviews/account/accountWebview.ts View File

@ -1,7 +1,6 @@
import { Disposable, window } from 'vscode'; import { Disposable, window } from 'vscode';
import { getAvatarUriFromGravatarEmail } from '../../../avatars'; import { getAvatarUriFromGravatarEmail } from '../../../avatars';
import type { Container } from '../../../container'; import type { Container } from '../../../container';
import type { RepositoriesVisibility } from '../../../git/gitProviderService';
import type { Subscription } from '../../../subscription'; import type { Subscription } from '../../../subscription';
import { registerCommand } from '../../../system/command'; import { registerCommand } from '../../../system/command';
import type { Deferrable } from '../../../system/function'; import type { Deferrable } from '../../../system/function';
@ -30,7 +29,16 @@ export class AccountWebviewProvider implements WebviewProvider {
} }
registerCommands(): Disposable[] { registerCommands(): Disposable[] {
return [registerCommand(`${this.host.id}.refresh`, () => this.host.refresh(true), this)];
return [
registerCommand(
`${this.host.id}.refresh`,
async () => {
await this.validateSubscriptionCore(true);
await this.host.refresh(true);
},
this,
),
];
} }
includeBootstrap(): Promise<State> { includeBootstrap(): Promise<State> {
@ -59,11 +67,6 @@ export class AccountWebviewProvider implements WebviewProvider {
queueMicrotask(() => void this.validateSubscription()); queueMicrotask(() => void this.validateSubscription());
} }
private async getRepoVisibility(): Promise<RepositoriesVisibility> {
const visibility = await this.container.git.visibility();
return visibility;
}
private async getSubscription(subscription?: Subscription) { private async getSubscription(subscription?: Subscription) {
const sub = subscription ?? (await this.container.subscription.getSubscription(true)); const sub = subscription ?? (await this.container.subscription.getSubscription(true));
@ -113,9 +116,9 @@ export class AccountWebviewProvider implements WebviewProvider {
} }
private _validating: Promise<void> | undefined; private _validating: Promise<void> | undefined;
private async validateSubscriptionCore() {
if (this._validating == null) {
this._validating = this.container.subscription.validate();
private async validateSubscriptionCore(force?: boolean) {
if (this._validating == null || force) {
this._validating = this.container.subscription.validate({ force: force });
try { try {
await this._validating; await this._validating;
} finally { } finally {

+ 2
- 0
src/subscription.ts View File

@ -22,6 +22,8 @@ export interface Subscription {
previewTrial?: SubscriptionPreviewTrial; previewTrial?: SubscriptionPreviewTrial;
state: SubscriptionState; state: SubscriptionState;
lastValidatedAt?: number;
} }
export interface SubscriptionPlan { export interface SubscriptionPlan {

Loading…
Cancel
Save