@ -29,8 +29,9 @@ import {
getSubscriptionPlanPriority ,
getSubscriptionPlanPriority ,
getSubscriptionTimeRemaining ,
getSubscriptionTimeRemaining ,
getTimeRemaining ,
getTimeRemaining ,
isPaidSubscriptionPlan ,
isSubscriptionExpired ,
isSubscriptionExpired ,
isSubscriptionPaidPlan ,
isSubscriptionPreviewTrialExpired ,
isSubscriptionTrial ,
isSubscriptionTrial ,
Subscription ,
Subscription ,
SubscriptionPlanId ,
SubscriptionPlanId ,
@ -43,6 +44,7 @@ import { debug, log } from '../../system/decorators/log';
import { memoize } from '../../system/decorators/memoize' ;
import { memoize } from '../../system/decorators/memoize' ;
import { once } from '../../system/function' ;
import { once } from '../../system/function' ;
import { pluralize } from '../../system/string' ;
import { pluralize } from '../../system/string' ;
import { openWalkthrough } from '../../system/utils' ;
// TODO: What user-agent should we use?
// TODO: What user-agent should we use?
const userAgent = 'Visual-Studio-Code-GitLens' ;
const userAgent = 'Visual-Studio-Code-GitLens' ;
@ -155,19 +157,21 @@ export class SubscriptionService implements Disposable {
void this . container . viewCommands ;
void this . container . viewCommands ;
return [
return [
commands . registerCommand ( 'gitlens.premium.login' , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( 'gitlens.premium.loginOrSignUp' , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( 'gitlens.premium.signUp' , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( 'gitlens.premium.logout' , ( ) = > this . logout ( ) ) ,
commands . registerCommand ( 'gitlens.premium.startPreview' , ( ) = > this . startPreview ( ) ) ,
commands . registerCommand ( 'gitlens.premium.purchase' , ( ) = > this . purchase ( ) ) ,
commands . registerCommand ( Commands . PremiumLearn , ( ) = > this . learn ( ) ) ,
commands . registerCommand ( Commands . PremiumLogin , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( Commands . PremiumLoginOrSignUp , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( Commands . PremiumSignUp , ( ) = > this . loginOrSignUp ( ) ) ,
commands . registerCommand ( Commands . PremiumLogout , ( ) = > this . logout ( ) ) ,
commands . registerCommand ( Commands . PremiumStartPreviewTrial , ( ) = > this . startPreviewTrial ( ) ) ,
commands . registerCommand ( Commands . PremiumPurchase , ( ) = > this . purchase ( ) ) ,
// TODO@eamodio remove before release
commands . registerCommand ( 'gitlens.premium.reset' , ( ) = > this . logout ( true ) ) ,
commands . registerCommand ( 'gitlens.premium.reset' , ( ) = > this . logout ( true ) ) ,
commands . registerCommand ( 'gitlens.premium.resendVerification' , ( ) = > this . resendVerification ( ) ) ,
commands . registerCommand ( 'gitlens.premium.validate' , ( ) = > this . validate ( ) ) ,
commands . registerCommand ( Commands . PremiumResendVerification , ( ) = > this . resendVerification ( ) ) ,
commands . registerCommand ( Commands . PremiumValidate , ( ) = > this . validate ( ) ) ,
commands . registerCommand ( 'gitlens.premium.showPlans' , ( ) = > this . showPlans ( ) ) ,
commands . registerCommand ( Commands . PremiumShowPlans , ( ) = > this . showPlans ( ) ) ,
] ;
] ;
}
}
@ -176,6 +180,11 @@ export class SubscriptionService implements Disposable {
return this . _subscription ;
return this . _subscription ;
}
}
@debug ( )
learn ( ) : void {
void openWalkthrough ( this . container . context . extension . id , 'gitlens.premium' ) ;
}
@gate ( )
@gate ( )
@log ( )
@log ( )
async loginOrSignUp ( ) : Promise < boolean > {
async loginOrSignUp ( ) : Promise < boolean > {
@ -298,9 +307,9 @@ export class SubscriptionService implements Disposable {
@gate ( )
@gate ( )
@log ( )
@log ( )
async startPreview ( ) : Promise < void > {
let { plan , preview } = this . _subscription ;
if ( preview != null || plan . effective . id !== SubscriptionPlanId . Free ) {
async startPreviewTrial ( ) : Promise < void > {
let { plan , previewTrial } = this . _subscription ;
if ( previewTrial != null || plan . effective . id !== SubscriptionPlanId . Free ) {
if ( plan . effective . id === SubscriptionPlanId . Free ) {
if ( plan . effective . id === SubscriptionPlanId . Free ) {
const confirm = { title : 'Extend Trial' } ;
const confirm = { title : 'Extend Trial' } ;
const cancel = { title : 'Cancel' } ;
const cancel = { title : 'Cancel' } ;
@ -331,7 +340,7 @@ export class SubscriptionService implements Disposable {
days = 0 ;
days = 0 ;
}
}
preview = {
previewTrial = {
startedOn : startedOn.toISOString ( ) ,
startedOn : startedOn.toISOString ( ) ,
expiresOn : expiresOn.toISOString ( ) ,
expiresOn : expiresOn.toISOString ( ) ,
} ;
} ;
@ -342,7 +351,7 @@ export class SubscriptionService implements Disposable {
. . . this . _subscription . plan ,
. . . this . _subscription . plan ,
effective : getSubscriptionPlan ( SubscriptionPlanId . Pro , startedOn , expiresOn ) ,
effective : getSubscriptionPlan ( SubscriptionPlanId . Pro , startedOn , expiresOn ) ,
} ,
} ,
preview : preview ,
previewTrial : previewTrial ,
} ) ;
} ) ;
void window . showInformationMessage ( ` You can now try premium GitLens features for ${ days } days. ` ) ;
void window . showInformationMessage ( ` You can now try premium GitLens features for ${ days } days. ` ) ;
@ -374,8 +383,8 @@ export class SubscriptionService implements Disposable {
vscodeEdition : env.appName ,
vscodeEdition : env.appName ,
vscodeHost : env.appHost ,
vscodeHost : env.appHost ,
vscodeVersion : codeVersion ,
vscodeVersion : codeVersion ,
previewStartedOn : this._subscription.preview?.startedOn ,
previewExpiresOn : this._subscription.preview?.expiresOn ,
previewStartedOn : this._subscription.previewTrial ?.startedOn ,
previewExpiresOn : this._subscription.previewTrial ?.expiresOn ,
} ;
} ;
const rsp = await fetch ( Uri . joinPath ( this . baseApiUri , 'gitlens/checkin' ) . toString ( ) , {
const rsp = await fetch ( Uri . joinPath ( this . baseApiUri , 'gitlens/checkin' ) . toString ( ) , {
@ -572,13 +581,13 @@ export class SubscriptionService implements Disposable {
// If the effective plan is Free, then check if the preview has expired, if not apply it
// If the effective plan is Free, then check if the preview has expired, if not apply it
if (
if (
subscription . plan . effective . id === SubscriptionPlanId . Free &&
subscription . plan . effective . id === SubscriptionPlanId . Free &&
subscription . preview != null &&
( getTimeRemaining ( subscription . preview . expiresOn ) ? ? 0 ) > 0
subscription . previewTrial != null &&
( getTimeRemaining ( subscription . previewTrial . expiresOn ) ? ? 0 ) > 0
) {
) {
( subscription . plan as PickMutable < Subscription [ ' plan ' ] , ' effective ' > ) . effective = getSubscriptionPlan (
( subscription . plan as PickMutable < Subscription [ ' plan ' ] , ' effective ' > ) . effective = getSubscriptionPlan (
SubscriptionPlanId . Pro ,
SubscriptionPlanId . Pro ,
new Date ( subscription . preview . startedOn ) ,
new Date ( subscription . preview . expiresOn ) ,
new Date ( subscription . previewTrial . startedOn ) ,
new Date ( subscription . previewTrial . expiresOn ) ,
) ;
) ;
}
}
@ -620,22 +629,25 @@ export class SubscriptionService implements Disposable {
queueMicrotask ( async ( ) = > {
queueMicrotask ( async ( ) = > {
const { allowed , subscription } = await this . container . git . access ( ) ;
const { allowed , subscription } = await this . container . git . access ( ) ;
void setContext (
void setContext (
ContextKeys . PremiumUpgrade Required ,
ContextKeys . PremiumRequired ,
allowed
allowed
? false
? false
: subscription . required != null && isPaid SubscriptionPlan ( subscription . required )
: subscription . required != null && isSubscriptionPaid Plan ( subscription . required )
? 'paid'
? 'paid'
: 'free+' ,
: 'free+' ,
) ;
) ;
} ) ;
} ) ;
const {
const {
account ,
plan : { actual } ,
plan : { actual } ,
} = this . _subscription ;
} = this . _subscription ;
void setContext ( ContextKeys . Premium , actual . id ) ;
void setContext ( ContextKeys . PremiumPaid , isPaidSubscriptionPlan ( actual . id ) ) ;
void setContext ( ContextKeys . PremiumRequiresVerification , this . _subscription . account ? . verified === false ) ;
void setContext ( ContextKeys . Premium , actual . id != SubscriptionPlanId . Free ? actual.id : undefined ) ;
void setContext ( ContextKeys . PremiumPaid , isSubscriptionPaidPlan ( actual . id ) ) ;
void setContext ( ContextKeys . PremiumRequiresVerification , account ? . verified === false ) ;
void setContext ( ContextKeys . PremiumTrial , isSubscriptionTrial ( this . _subscription ) ) ;
void setContext ( ContextKeys . PremiumPreviewTrialExpired , isSubscriptionPreviewTrialExpired ( this . _subscription ) ) ;
}
}
private updateStatusBar ( pending : boolean = false ) : void {
private updateStatusBar ( pending : boolean = false ) : void {