diff --git a/package.json b/package.json index ae9dd3b4fd94f..801d6810b1362 100644 --- a/package.json +++ b/package.json @@ -5662,13 +5662,8 @@ "category": "GitLens" }, { - "command": "gitlens.plus.simulateSubscriptionState", - "title": "Simulate Subscription State (Debugging)", - "category": "GitLens" - }, - { - "command": "gitlens.plus.restoreSubscriptionState", - "title": "Restore Subscription State (Debugging)", + "command": "gitlens.plus.simulateSubscription", + "title": "Simulate Subscription (Debugging)", "category": "GitLens" }, { @@ -9803,11 +9798,7 @@ "when": "gitlens:enabled" }, { - "command": "gitlens.plus.simulateSubscriptionState", - "when": "gitlens:enabled && gitlens:debugging" - }, - { - "command": "gitlens.plus.restoreSubscriptionState", + "command": "gitlens.plus.simulateSubscription", "when": "gitlens:enabled && gitlens:debugging" }, { diff --git a/src/commands/resets.ts b/src/commands/resets.ts index 3dab98f72647f..3c4c1429e4c49 100644 --- a/src/commands/resets.ts +++ b/src/commands/resets.ts @@ -13,7 +13,6 @@ const resetTypes = [ 'ai', 'avatars', 'integrations', - 'plus', 'repositoryAccess', 'suppressedWarnings', 'usageTracking', @@ -74,19 +73,6 @@ export class ResetCommand extends Command { }, ]; - if (this.container.debugging) { - items.splice( - 0, - 0, - { - label: 'Subscription Reset', - detail: 'Resets the stored subscription', - item: 'plus', - }, - createQuickPickSeparator(), - ); - } - // create a quick pick with options to clear all the different resets that GitLens supports const pick = await window.showQuickPick(items, { title: 'Reset Stored Data', @@ -94,7 +80,6 @@ export class ResetCommand extends Command { }); if (pick?.item == null) return; - if (pick.item === 'plus' && !this.container.debugging) return; const confirm: MessageItem = { title: 'Reset' }; const cancel: MessageItem = { title: 'Cancel', isCloseAffordance: true }; @@ -170,10 +155,6 @@ export class ResetCommand extends Command { await this.container.integrations.reset(); break; - case 'plus': - await this.container.subscription.logout(true, undefined); - break; - case 'repositoryAccess': await this.container.git.clearAllRepoVisibilityCaches(); break; diff --git a/src/constants.commands.ts b/src/constants.commands.ts index 3185450cb016f..c079c984388ec 100644 --- a/src/constants.commands.ts +++ b/src/constants.commands.ts @@ -151,8 +151,7 @@ export const enum Commands { PlusStartPreviewTrial = 'gitlens.plus.startPreviewTrial', PlusUpgrade = 'gitlens.plus.upgrade', PlusValidate = 'gitlens.plus.validate', - PlusSimulateSubscriptionState = 'gitlens.plus.simulateSubscriptionState', - PlusRestoreSubscriptionState = 'gitlens.plus.restoreSubscriptionState', + PlusSimulateSubscription = 'gitlens.plus.simulateSubscription', QuickOpenFileHistory = 'gitlens.quickOpenFileHistory', RefreshLaunchpad = 'gitlens.launchpad.refresh', RefreshGraph = 'gitlens.graph.refresh', diff --git a/src/plus/gk/account/__debug__accountDebug.ts b/src/plus/gk/account/__debug__accountDebug.ts index 2bf9b325f57d1..2cf036ddb86dd 100644 --- a/src/plus/gk/account/__debug__accountDebug.ts +++ b/src/plus/gk/account/__debug__accountDebug.ts @@ -1,72 +1,262 @@ -import { window } from 'vscode'; +import type { Disposable } from 'vscode'; +import { ThemeIcon, window } from 'vscode'; import { Commands } from '../../../constants.commands'; import { SubscriptionPlanId, SubscriptionState } from '../../../constants.subscription'; import type { Container } from '../../../container'; +import type { QuickPickItemOfT } from '../../../quickpicks/items/common'; +import { createQuickPickSeparator } from '../../../quickpicks/items/common'; import { registerCommand } from '../../../system/vscode/command'; import { configuration } from '../../../system/vscode/configuration'; import type { GKCheckInResponse, GKLicenses, GKLicenseType, GKUser } from '../checkin'; import { getSubscriptionFromCheckIn } from '../checkin'; -import { getPreviewTrialAndDays } from '../utils'; -import { getSubscriptionPlan } from './subscription'; +import { getPreviewSubscription } from './subscription'; import type { SubscriptionService } from './subscriptionService'; +type SubscriptionServiceFacade = { + getSubscription: () => SubscriptionService['_subscription']; + overrideSession: (session: SubscriptionService['_session']) => void; + restoreSession: () => void; + onDidCheckIn: SubscriptionService['_onDidCheckIn']; + changeSubscription: SubscriptionService['changeSubscription']; + getStoredSubscription: SubscriptionService['getStoredSubscription']; +}; + +export function registerAccountDebug(container: Container, service: SubscriptionServiceFacade): void { + new AccountDebug(container, service); +} + +type SimulateQuickPickItem = QuickPickItemOfT< + | { state: null; reactivatedTrial?: never; expiredPaid?: never; planId?: never } + | { + state: Exclude; + reactivatedTrial?: never; + expiredPaid?: never; + planId?: never; + } + | { + state: SubscriptionState.FreePlusInTrial; + reactivatedTrial?: boolean; + expiredPaid?: never; + planId?: never; + } + | { + state: SubscriptionState.Paid; + reactivatedTrial?: never; + expiredPaid?: boolean; + planId?: SubscriptionPlanId.Pro | SubscriptionPlanId.Teams | SubscriptionPlanId.Enterprise; + } +>; + class AccountDebug { + private simulatingPick: SimulateQuickPickItem | undefined; + constructor( private readonly container: Container, - private readonly subscriptionStub: { - getSession: () => SubscriptionService['_session']; - getSubscription: () => SubscriptionService['_subscription']; - onDidCheckIn: SubscriptionService['_onDidCheckIn']; - changeSubscription: SubscriptionService['changeSubscription']; - getStoredSubscription: SubscriptionService['getStoredSubscription']; - }, + private readonly service: SubscriptionServiceFacade, ) { this.container.context.subscriptions.push( - registerCommand(Commands.PlusSimulateSubscriptionState, () => this.simulateSubscriptionState()), - registerCommand(Commands.PlusRestoreSubscriptionState, () => this.restoreSubscriptionState()), + registerCommand(Commands.PlusSimulateSubscription, () => this.showSimulator()), ); } - private async simulateSubscriptionState() { - if ( - !this.container.debugging || - this.subscriptionStub.getSession() == null || - this.subscriptionStub.getSubscription() == null - ) { - return; - } - - // Show a quickpick to select a subscription state to simulate - const picks: { label: string; state: SubscriptionState; reactivatedTrial?: boolean; expiredPaid?: boolean }[] = - [ - { label: 'Free', state: SubscriptionState.Free }, - { label: 'Free In Preview Trial', state: SubscriptionState.FreeInPreviewTrial }, - { label: 'Free Preview Trial Expired', state: SubscriptionState.FreePreviewTrialExpired }, - { label: 'Free+ In Trial', state: SubscriptionState.FreePlusInTrial }, + // Show a quickpick to select a subscription state to simulate + private async showSimulator() { + function getItemsAndPicked( + pick: SimulateQuickPickItem | undefined, + ): [SimulateQuickPickItem[], SimulateQuickPickItem | undefined] { + const items: SimulateQuickPickItem[] = [ + { + label: 'Community', + description: 'Community, no account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.Free }, + }, + createQuickPickSeparator('Preview'), + { + label: 'Pro Preview', + description: 'Pro, no account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.FreeInPreviewTrial }, + }, + { + label: 'Pro Preview (Expired)', + description: 'Community, no account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.FreePreviewTrialExpired }, + }, + createQuickPickSeparator('Account'), + { + label: 'Verification Required', + description: 'Community, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.VerificationRequired }, + }, + createQuickPickSeparator('Trial'), + { + label: 'Pro Trial', + description: 'Pro, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.FreePlusInTrial }, + }, { - label: 'Free+ In Trial (Reactivated)', - state: SubscriptionState.FreePlusInTrial, - reactivatedTrial: true, + label: 'Pro Trial (Reactivated)', + description: 'Pro, account', + iconPath: new ThemeIcon('blank'), + item: { + state: SubscriptionState.FreePlusInTrial, + reactivatedTrial: true, + }, }, - { label: 'Free+ Trial Expired', state: SubscriptionState.FreePlusTrialExpired }, { - label: 'Free+ Trial Reactivation Eligible', - state: SubscriptionState.FreePlusTrialReactivationEligible, + label: 'Pro Trial (Expired)', + description: 'Community, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.FreePlusTrialExpired }, + }, + { + label: 'Pro Trial (Reactivation Eligible)', + description: 'Community, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.FreePlusTrialReactivationEligible }, + }, + createQuickPickSeparator('Paid'), + { + label: 'Pro', + description: 'Pro, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.Paid, planId: SubscriptionPlanId.Pro }, + }, + { + label: 'Teams', + description: 'Teams, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.Paid, planId: SubscriptionPlanId.Teams }, + }, + { + label: 'Enterprise', + description: 'Enterprise, no account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.Paid, planId: SubscriptionPlanId.Enterprise }, }, - { label: 'Paid', state: SubscriptionState.Paid }, // TODO: Update this subscription state once we have a "paid expired" state availale - { label: 'Paid Expired', state: SubscriptionState.Paid, expiredPaid: true }, - { label: 'Verification Required', state: SubscriptionState.VerificationRequired }, + { + label: 'Paid (Expired)', + description: 'Community, account', + iconPath: new ThemeIcon('blank'), + item: { state: SubscriptionState.Paid, expiredPaid: true }, + }, ]; - const pick = await window.showQuickPick(picks, { - title: 'Simulate Subscription State', - placeHolder: 'Select the subscription state to simulate', - }); - if (pick == null) return; - const { state: subscriptionState, reactivatedTrial, expiredPaid } = pick; + let picked; + if (pick != null) { + picked = items.find(i => i.label === pick?.label); + if (picked != null) { + picked.iconPath = new ThemeIcon('check'); + } + + items.splice( + 0, + 0, + { + label: 'End Simulation', + description: 'Restores stored subscription', + iconPath: new ThemeIcon('beaker-stop'), + item: { state: null }, + }, + createQuickPickSeparator(), + ); + } + + return [items, picked]; + } + + const quickpick = window.createQuickPick(); + quickpick.ignoreFocusOut = true; + + const disposables: Disposable[] = []; + + try { + await new Promise(resolve => { + disposables.push( + quickpick.onDidHide(() => resolve()), + quickpick.onDidAccept(async () => { + const [item] = quickpick.activeItems; - const organizations = (await this.container.organizations.getOrganizations()) ?? []; + const close = await this.startSimulation(item); + if (close) { + resolve(); + + return; + } + + const [items, picked] = getItemsAndPicked(this.simulatingPick); + quickpick.items = items; + quickpick.activeItems = picked ? [picked] : []; + }), + ); + + quickpick.title = 'Subscription Simulator'; + quickpick.placeholder = 'Select the subscription state to simulate'; + + const [items, picked] = getItemsAndPicked(this.simulatingPick); + quickpick.items = items; + quickpick.activeItems = picked ? [picked] : []; + + quickpick.show(); + }); + } finally { + quickpick.dispose(); + disposables.forEach(d => void d.dispose()); + } + } + + private endSimulation() { + this.simulatingPick = undefined; + + this.service.restoreSession(); + this.service.changeSubscription(this.service.getStoredSubscription(), { store: false }); + } + + private async startSimulation(pick: SimulateQuickPickItem | undefined): Promise { + this.simulatingPick = pick; + if (pick?.item == null) return true; + const { item } = pick; + if (item.state == null) { + this.endSimulation(); + return true; + } + + const { state, reactivatedTrial, expiredPaid, planId } = item; + + switch (state) { + case SubscriptionState.Free: + case SubscriptionState.FreeInPreviewTrial: + case SubscriptionState.FreePreviewTrialExpired: + this.service.overrideSession(null); + + this.service.changeSubscription( + state === SubscriptionState.Free + ? undefined + : getPreviewSubscription(state === SubscriptionState.FreePreviewTrialExpired ? 0 : 3), + { store: false }, + ); + return false; + } + + this.service.restoreSession(); + + const subscription = this.service.getStoredSubscription(); + if (subscription?.account == null) { + void window.showErrorMessage("Can't simulate state, without an account"); + + this.endSimulation(); + return true; + } + + const organizations = + (await this.container.organizations.getOrganizations({ + userId: subscription.account.id, + })) ?? []; let activeOrganizationId = configuration.get('gitKraken.activeOrganizationId') ?? undefined; if (activeOrganizationId === '' || (activeOrganizationId == null && organizations.length === 1)) { activeOrganizationId = organizations[0].id; @@ -74,72 +264,35 @@ class AccountDebug { const simulatedCheckInData: GKCheckInResponse = getSimulatedCheckInResponse( { - id: this.subscriptionStub.getSubscription()?.account?.id ?? '', - name: '', - email: '', - status: subscriptionState === SubscriptionState.VerificationRequired ? 'pending' : 'activated', + id: subscription.account.id, + name: 'Simulated User', + email: 'simulated@user.com', + status: state === SubscriptionState.VerificationRequired ? 'pending' : 'activated', createdDate: new Date().toISOString(), }, - subscriptionState, - 'gitkraken_v1-pro', + state, + planId === 'enterprise' + ? 'gitkraken_v1-hosted-enterprise' + : planId === 'teams' + ? 'gitkraken_v1-teams' + : 'gitkraken_v1-pro', { organizationId: activeOrganizationId, trial: { reactivatedTrial: reactivatedTrial }, expiredPaid: expiredPaid, }, ); - this.subscriptionStub.onDidCheckIn.fire(); - let simulatedSubscription = getSubscriptionFromCheckIn( + + this.service.onDidCheckIn.fire(); + const simulatedSubscription = getSubscriptionFromCheckIn( simulatedCheckInData, organizations, activeOrganizationId, ); - if ( - subscriptionState === SubscriptionState.FreeInPreviewTrial || - subscriptionState === SubscriptionState.FreePreviewTrialExpired - ) { - simulatedSubscription = { - ...simulatedSubscription, - plan: { - ...simulatedSubscription.plan, - actual: getSubscriptionPlan( - SubscriptionPlanId.Free, - false, - 0, - undefined, - new Date(simulatedSubscription.plan.actual.startedOn), - ), - effective: getSubscriptionPlan( - SubscriptionPlanId.Free, - false, - 0, - undefined, - new Date(simulatedSubscription.plan.effective.startedOn), - ), - }, - }; - const { previewTrial: simulatedPreviewTrial } = getPreviewTrialAndDays(); - if (subscriptionState === SubscriptionState.FreePreviewTrialExpired) { - simulatedPreviewTrial.startedOn = new Date(Date.now() - 2000).toISOString(); - simulatedPreviewTrial.expiresOn = new Date(Date.now() - 1000).toISOString(); - } - - simulatedSubscription.previewTrial = simulatedPreviewTrial; - } + this.service.changeSubscription({ ...subscription, ...simulatedSubscription }, { store: false }); - this.subscriptionStub.changeSubscription( - { - ...this.subscriptionStub.getSubscription(), - ...simulatedSubscription, - }, - { store: false }, - ); - } - - private restoreSubscriptionState() { - if (!this.container.debugging || this.subscriptionStub.getSession() == null) return; - this.subscriptionStub.changeSubscription(this.subscriptionStub.getStoredSubscription(), { store: false }); + return false; } } @@ -260,18 +413,3 @@ function getSimulatedCheckInResponse( : undefined, }; } - -export function registerAccountDebug( - container: Container, - subscriptionStub: { - getSession: () => SubscriptionService['_session']; - getSubscription: () => SubscriptionService['_subscription']; - onDidCheckIn: SubscriptionService['_onDidCheckIn']; - changeSubscription: SubscriptionService['changeSubscription']; - getStoredSubscription: SubscriptionService['getStoredSubscription']; - }, -): void { - if (!container.debugging) return; - - new AccountDebug(container, subscriptionStub); -} diff --git a/src/plus/gk/account/subscription.ts b/src/plus/gk/account/subscription.ts index c996895c36da8..de87c5f6facbd 100644 --- a/src/plus/gk/account/subscription.ts +++ b/src/plus/gk/account/subscription.ts @@ -1,6 +1,6 @@ // NOTE@eamodio This file is referenced in the webviews to we can't use anything vscode or other imports that aren't available in the webviews import { SubscriptionPlanId, SubscriptionState } from '../../../constants.subscription'; -import { getDateDifference } from '../../../system/date'; +import { createFromDateDelta, getDateDifference } from '../../../system/date'; import type { Organization } from './organization'; export const SubscriptionUpdatedUriPathPrefix = 'did-update-subscription'; @@ -251,3 +251,56 @@ export function hasAccountFromSubscriptionState(state: SubscriptionState | undef export function assertSubscriptionState( subscription: Optional, ): asserts subscription is Subscription {} + +export function getCommunitySubscription(subscription?: Subscription): Subscription { + return { + ...subscription, + plan: { + actual: getSubscriptionPlan( + SubscriptionPlanId.Free, + false, + 0, + undefined, + subscription?.plan?.actual?.startedOn != null + ? new Date(subscription.plan.actual.startedOn) + : undefined, + ), + effective: getSubscriptionPlan( + SubscriptionPlanId.Free, + false, + 0, + undefined, + subscription?.plan?.actual?.startedOn != null + ? new Date(subscription.plan.actual.startedOn) + : undefined, + ), + }, + account: undefined, + activeOrganization: undefined, + state: SubscriptionState.Free, + }; +} + +export function getPreviewSubscription(days: number, subscription?: Subscription): Subscription { + const startedOn = new Date(); + + let expiresOn = new Date(startedOn); + if (days !== 0) { + // Normalize the date to just before midnight on the same day + expiresOn.setHours(23, 59, 59, 999); + expiresOn = createFromDateDelta(expiresOn, { days: days }); + } + + subscription ??= getCommunitySubscription(); + return { + ...subscription, + plan: { + ...subscription.plan, + effective: getSubscriptionPlan(SubscriptionPlanId.Pro, false, 0, undefined, startedOn, expiresOn), + }, + previewTrial: { + startedOn: startedOn.toISOString(), + expiresOn: expiresOn.toISOString(), + }, + }; +} diff --git a/src/plus/gk/account/subscriptionService.ts b/src/plus/gk/account/subscriptionService.ts index 02c42440911f2..81cc65ec58e57 100644 --- a/src/plus/gk/account/subscriptionService.ts +++ b/src/plus/gk/account/subscriptionService.ts @@ -48,7 +48,7 @@ import { openUrl } from '../../../system/vscode/utils'; import type { GKCheckInResponse } from '../checkin'; import { getSubscriptionFromCheckIn } from '../checkin'; import type { ServerConnection } from '../serverConnection'; -import { ensurePlusFeaturesEnabled, getPreviewTrialAndDays } from '../utils'; +import { ensurePlusFeaturesEnabled } from '../utils'; import { LoginUriPathPrefix } from './authenticationConnection'; import { authenticationProviderScopes } from './authenticationProvider'; import type { Organization } from './organization'; @@ -57,6 +57,8 @@ import type { Subscription } from './subscription'; import { assertSubscriptionState, computeSubscriptionState, + getCommunitySubscription, + getPreviewSubscription, getSubscriptionPlan, getSubscriptionPlanName, getSubscriptionStateString, @@ -144,7 +146,7 @@ export class SubscriptionService implements Disposable { if (session != null && e.removed?.some(s => s.id === session.id)) { this._session = undefined; this._sessionPromise = undefined; - void this.logout(undefined, undefined); + void this.logout(undefined); return; } @@ -171,20 +173,53 @@ export class SubscriptionService implements Disposable { ...this.registerCommands(), ); this.updateContext(); + if (DEBUG) { - void import(/* webpackChunkName: "__debug__" */ './__debug__accountDebug').then(m => + void import(/* webpackChunkName: "__debug__" */ './__debug__accountDebug').then(m => { + let restore: { session: AuthenticationSession | null | undefined } | undefined; + + function setSession(this: SubscriptionService, session: AuthenticationSession | null | undefined) { + this._sessionPromise = undefined; + if (session === this._session) return; + + const previous = this._session; + this._session = session; + + // Replace the next `onAuthenticationChanged` handler to avoid our own trigger below + const fn = this.onAuthenticationChanged; + // eslint-disable-next-line @typescript-eslint/require-await + this.onAuthenticationChanged = async () => { + this.onAuthenticationChanged = fn; + }; + + // @ts-expect-error - fragile, but don't want to expose this as it is only for debugging + this.container.accountAuthentication._onDidChangeSessions.fire({ + added: previous == null && session != null ? [session] : [], + removed: previous != null && session == null ? [previous] : [], + changed: previous != null && session != null ? [session] : [], + }); + } + m.registerAccountDebug(this.container, { - getSession: () => { - return this._session; + getSubscription: () => this._subscription, + overrideSession: (session: AuthenticationSession | null | undefined) => { + restore ??= { session: this._session }; + + setSession.call(this, session); }, - getSubscription: () => { - return this._subscription; + restoreSession: () => { + if (restore == null) return; + + const { session } = restore; + restore = undefined; + + setSession.call(this, session); }, onDidCheckIn: this._onDidCheckIn, changeSubscription: this.changeSubscription.bind(this), getStoredSubscription: this.getStoredSubscription.bind(this), - }), - ); + }); + }); } } @@ -198,7 +233,7 @@ export class SubscriptionService implements Disposable { return [ registerCommand(Commands.PlusLogin, (src?: Source) => this.loginOrSignUp(false, src)), registerCommand(Commands.PlusSignUp, (src?: Source) => this.loginOrSignUp(true, src)), - registerCommand(Commands.PlusLogout, (src?: Source) => this.logout(undefined, src)), + registerCommand(Commands.PlusLogout, (src?: Source) => this.logout(src)), registerCommand(Commands.GKSwitchOrganization, () => this.switchOrganization()), registerCommand(Commands.PlusManage, (src?: Source) => this.manage(src)), @@ -394,7 +429,7 @@ export class SubscriptionService implements Disposable { const session = await this.ensureSession(false); if (session != null) { - await this.logout(undefined, source); + await this.logout(source); } return this.loginCore({ signIn: authentication, source: source }); @@ -423,15 +458,15 @@ export class SubscriptionService implements Disposable { } @log() - async logout(reset: boolean = false, source: Source | undefined): Promise { + async logout(source: Source | undefined): Promise { if (this.container.telemetry.enabled) { this.container.telemetry.sendEvent('subscription/action', { action: 'sign-out' }, source); } - return this.logoutCore(reset); + return this.logoutCore(); } - private async logoutCore(reset: boolean = false): Promise { + private async logoutCore(): Promise { this.connection.resetRequestExceptionCount(); this._lastValidatedDate = undefined; if (this._validationTimer != null) { @@ -450,37 +485,7 @@ export class SubscriptionService implements Disposable { void this.container.accountAuthentication.removeSessionsByScopes(authenticationProviderScopes); } - if (reset && this.container.debugging) { - this.changeSubscription(undefined); - - return; - } - - this.changeSubscription({ - ...this._subscription, - plan: { - actual: getSubscriptionPlan( - SubscriptionPlanId.Free, - false, - 0, - undefined, - this._subscription.plan?.actual?.startedOn != null - ? new Date(this._subscription.plan.actual.startedOn) - : undefined, - ), - effective: getSubscriptionPlan( - SubscriptionPlanId.Free, - false, - 0, - undefined, - this._subscription.plan?.effective?.startedOn != null - ? new Date(this._subscription.plan.actual.startedOn) - : undefined, - ), - }, - account: undefined, - activeOrganization: undefined, - }); + this.changeSubscription(getCommunitySubscription(this._subscription)); } @log() @@ -676,7 +681,7 @@ export class SubscriptionService implements Disposable { this.container.telemetry.sendEvent('subscription/action', { action: 'start-preview-trial' }, source); } - let { plan, previewTrial } = this._subscription; + const { plan, previewTrial } = this._subscription; if (previewTrial != null) { void this.showAccountView(); @@ -703,17 +708,9 @@ export class SubscriptionService implements Disposable { // Don't overwrite a trial that is already in progress if (isSubscriptionInProTrial(this._subscription)) return; - const { previewTrial: newPreviewTrial, days, startedOn, expiresOn } = getPreviewTrialAndDays(); - previewTrial = newPreviewTrial; - - this.changeSubscription({ - ...this._subscription, - plan: { - ...this._subscription.plan, - effective: getSubscriptionPlan(SubscriptionPlanId.Pro, false, 0, undefined, startedOn, expiresOn), - }, - previewTrial: previewTrial, - }); + const days = 3; + const subscription = getPreviewSubscription(days, this._subscription); + this.changeSubscription(subscription); setTimeout(async () => { const confirm: MessageItem = { title: 'Continue' }; diff --git a/src/plus/gk/utils.ts b/src/plus/gk/utils.ts index 96b1ccce0ade1..77140b34e4686 100644 --- a/src/plus/gk/utils.ts +++ b/src/plus/gk/utils.ts @@ -1,6 +1,5 @@ import type { MessageItem } from 'vscode'; import { window } from 'vscode'; -import { createFromDateDelta } from '../../system/date'; import { configuration } from '../../system/vscode/configuration'; import { getContext } from '../../system/vscode/context'; @@ -25,22 +24,3 @@ export async function ensurePlusFeaturesEnabled(): Promise { await configuration.updateEffective('plusFeatures.enabled', true); return true; } - -export function getPreviewTrialAndDays() { - const startedOn = new Date(); - - let expiresOn = new Date(startedOn); - // Normalize the date to just before midnight on the same day - expiresOn.setHours(23, 59, 59, 999); - expiresOn = createFromDateDelta(expiresOn, { days: 3 }); - - return { - previewTrial: { - startedOn: startedOn.toISOString(), - expiresOn: expiresOn.toISOString(), - }, - days: 3, - startedOn: startedOn, - expiresOn: expiresOn, - }; -} diff --git a/src/plus/integrations/integrationService.ts b/src/plus/integrations/integrationService.ts index 88cdd1ae8f618..da9701d2a724a 100644 --- a/src/plus/integrations/integrationService.ts +++ b/src/plus/integrations/integrationService.ts @@ -126,6 +126,7 @@ export class IntegrationService implements Disposable { } private onDidChangeSubscription(e: SubscriptionChangeEvent) { + // TODO is this correct? if (e.current?.account == null) { void this.syncCloudIntegrations(false); }