Browse Source

Fixes display states for worktrees welcome view

main
Eric Amodio 2 years ago
parent
commit
946bfd8ba3
14 changed files with 179 additions and 135 deletions
  1. +36
    -26
      package.json
  2. +1
    -1
      src/commands.ts
  3. +2
    -2
      src/commands/gitCommands.ts
  4. +13
    -6
      src/commands/quickCommand.steps.ts
  5. +5
    -4
      src/commands/walkthroughs.ts
  6. +14
    -2
      src/constants.ts
  7. +2
    -2
      src/errors.ts
  8. +2
    -2
      src/git/gitProviderService.ts
  9. +39
    -27
      src/premium/subscription/subscriptionService.ts
  10. +2
    -2
      src/quickpicks/items/directive.ts
  11. +12
    -7
      src/subscription.ts
  12. +22
    -20
      src/webviews/apps/premium/home/home.html
  13. +0
    -5
      src/webviews/premium/home/homeWebviewView.ts

+ 36
- 26
package.json View File

@ -64,15 +64,15 @@
"onView:gitlens.views.worktrees", "onView:gitlens.views.worktrees",
"onWebviewPanel:gitlens.welcome", "onWebviewPanel:gitlens.welcome",
"onWebviewPanel:gitlens.settings", "onWebviewPanel:gitlens.settings",
"onCommand:gitlens.premium.learn",
"onCommand:gitlens.premium.login", "onCommand:gitlens.premium.login",
"onCommand:gitlens.premium.loginOrSignUp", "onCommand:gitlens.premium.loginOrSignUp",
"onCommand:gitlens.premium.signUp", "onCommand:gitlens.premium.signUp",
"onCommand:gitlens.premium.logout", "onCommand:gitlens.premium.logout",
"onCommand:gitlens.premium.startPreview",
"onCommand:gitlens.premium.startPreviewTrial",
"onCommand:gitlens.premium.purchase", "onCommand:gitlens.premium.purchase",
"onCommand:gitlens.premium.reset", "onCommand:gitlens.premium.reset",
"onCommand:gitlens.getStarted", "onCommand:gitlens.getStarted",
"onCommand:gitlens.learnAboutPremium",
"onCommand:gitlens.showSettingsPage", "onCommand:gitlens.showSettingsPage",
"onCommand:gitlens.showSettingsPage#views", "onCommand:gitlens.showSettingsPage#views",
"onCommand:gitlens.showSettingsPage#branches-view", "onCommand:gitlens.showSettingsPage#branches-view",
@ -3591,6 +3591,11 @@
], ],
"commands": [ "commands": [
{ {
"command": "gitlens.premium.learn",
"title": "Learn about Premium Features",
"category": "GitLens"
},
{
"command": "gitlens.premium.login", "command": "gitlens.premium.login",
"title": "Login...", "title": "Login...",
"category": "GitLens Premium" "category": "GitLens Premium"
@ -3602,7 +3607,7 @@
}, },
{ {
"command": "gitlens.premium.signUp", "command": "gitlens.premium.signUp",
"title": "Sign Up for Free+...",
"title": "Create Free Account...",
"category": "GitLens Premium" "category": "GitLens Premium"
}, },
{ {
@ -3611,7 +3616,7 @@
"category": "GitLens Premium" "category": "GitLens Premium"
}, },
{ {
"command": "gitlens.premium.startPreview",
"command": "gitlens.premium.startPreviewTrial",
"title": "Try Premium Features...", "title": "Try Premium Features...",
"category": "GitLens Premium" "category": "GitLens Premium"
}, },
@ -3631,11 +3636,6 @@
"category": "GitLens" "category": "GitLens"
}, },
{ {
"command": "gitlens.learnAboutPremium",
"title": "Learn about Premium Features",
"category": "GitLens"
},
{
"command": "gitlens.showSettingsPage", "command": "gitlens.showSettingsPage",
"title": "Open Settings", "title": "Open Settings",
"category": "GitLens", "category": "GitLens",
@ -5893,23 +5893,23 @@
"commandPalette": [ "commandPalette": [
{ {
"command": "gitlens.premium.login", "command": "gitlens.premium.login",
"when": "gitlens:premium == free"
"when": "!gitlens:premium"
}, },
{ {
"command": "gitlens.premium.loginOrSignUp", "command": "gitlens.premium.loginOrSignUp",
"when": "gitlens:premium == free"
"when": "!gitlens:premium"
}, },
{ {
"command": "gitlens.premium.signUp", "command": "gitlens.premium.signUp",
"when": "gitlens:premium == free"
"when": "!gitlens:premium"
}, },
{ {
"command": "gitlens.premium.logout", "command": "gitlens.premium.logout",
"when": "gitlens:premium != free"
"when": "gitlens:premium"
}, },
{ {
"command": "gitlens.premium.startPreview",
"when": "gitlens:premium == free"
"command": "gitlens.premium.startPreviewTrial",
"when": "!gitlens:premium"
}, },
{ {
"command": "gitlens.premium.purchase", "command": "gitlens.premium.purchase",
@ -10547,37 +10547,47 @@
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "Worktrees allow you to easily work on different branches of a repository simultaneously. You can create multiple working trees, each of which can be opened in individual windows or all together in a single workspace."
"contents": "[Worktrees](command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22 \"Learn more about worktrees\") allow you to easily work on different branches of a repository simultaneously. You can create multiple working trees, each of which can be opened in individual windows or all together in a single workspace."
},
{
"view": "gitlens.views.worktrees",
"contents": "To use premium GitLens features, please verify the email for the account you created.\n\n[Resend verification email](command:gitlens.premium.resendVerification)",
"when": "gitlens:premium:requiresVerification"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "[Create Worktree...](command:gitlens.views.createWorktree)", "contents": "[Create Worktree...](command:gitlens.views.createWorktree)",
"when": "!gitlens:premium:upgradeRequired"
"when": "gitlens:premium || gitlens:premium:trial && !gitlens:premium:required && !gitlens:premium:requiresVerification"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "To use premium GitLens features, please verify the email for the account you created.\n\n[Resend verification email](command:gitlens.premium.resendVerification)",
"when": "gitlens:premium:requiresVerification"
"contents": "Worktrees are a premium feature, which require a free account for public repos.\n\nYou can try these premium features for free, without an account, for 3 days. All non-premium features will continue to be free without an account.",
"when": "!gitlens:premium:trial && gitlens:premium:required == free+ && !gitlens:premium:requiresVerification"
},
{
"view": "gitlens.views.worktrees",
"contents": "Worktrees are a premium feature, which require at least a Pro subscription for private repos.",
"when": "!gitlens:premium:trial && gitlens:premium:required == paid && !gitlens:premium:requiresVerification"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "Worktrees are a premium feature, which require a free account for public repos. [Learn more](https://dev.gitkraken.com/gitlens/premium-features).\n\nYou can try these premium features for free, without an account, for 3 days. All non-premium features will continue to be free without an account.",
"when": "gitlens:premium:upgradeRequired == free+ && !gitlens:premium:requiresVerification"
"contents": "[Learn about premium features](command:gitlens.premium.learn)",
"when": "!gitlens:premium:paid && gitlens:premium:required && !gitlens:premium:requiresVerification"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "[Try premium features now](command:gitlens.premium.startPreview)\n\n[Create a free account](command:gitlens.premium.loginOrSignUp)",
"when": "gitlens:premium:upgradeRequired == free+ && !gitlens:premium:requiresVerification"
"contents": "[Try premium features now](command:gitlens.premium.startPreviewTrial)",
"when": "!gitlens:premium && !gitlens:premium:trial && !gitlens:premium:previewTrial:expired"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "Worktrees are a premium feature, which require at least a Pro subscription for private repos. [Learn more](https://dev.gitkraken.com/gitlens/premium-features).",
"when": "gitlens:premium:upgradeRequired == paid"
"contents": "[Create a free account](command:gitlens.premium.loginOrSignUp)\n\nHave an existing account? [Sign in](command:gitlens.premium.loginOrSignUp)",
"when": "!gitlens:premium && gitlens:premium:required && !gitlens:premium:requiresVerification"
}, },
{ {
"view": "gitlens.views.worktrees", "view": "gitlens.views.worktrees",
"contents": "[Upgrade your account](command:gitlens.premium.purchase)", "contents": "[Upgrade your account](command:gitlens.premium.purchase)",
"when": "gitlens:premium:upgradeRequired == paid"
"when": "gitlens:premium && gitlens:premium:required == paid && !gitlens:premium:requiresVerification"
} }
], ],
"views": { "views": {

+ 1
- 1
src/commands.ts View File

@ -16,7 +16,6 @@ export * from './commands/diffWithRevision';
export * from './commands/diffWithRevisionFrom'; export * from './commands/diffWithRevisionFrom';
export * from './commands/diffWithWorking'; export * from './commands/diffWithWorking';
export * from './commands/externalDiff'; export * from './commands/externalDiff';
export * from './commands/getStarted';
export * from './commands/gitCommands'; export * from './commands/gitCommands';
export * from './commands/inviteToLiveShare'; export * from './commands/inviteToLiveShare';
export * from './commands/logging'; export * from './commands/logging';
@ -59,3 +58,4 @@ export * from './commands/switchMode';
export * from './commands/toggleCodeLens'; export * from './commands/toggleCodeLens';
export * from './commands/toggleFileAnnotations'; export * from './commands/toggleFileAnnotations';
export * from './commands/toggleLineBlame'; export * from './commands/toggleLineBlame';
export * from './commands/walkthroughs';

+ 2
- 2
src/commands/gitCommands.ts View File

@ -709,8 +709,8 @@ export class GitCommandsCommand extends Command {
void loadMore(); void loadMore();
return; return;
case Directive.StartPreview:
void Container.instance.subscription.startPreview();
case Directive.StartPreviewTrial:
void Container.instance.subscription.startPreviewTrial();
resolve(undefined); resolve(undefined);
return; return;

+ 13
- 6
src/commands/quickCommand.steps.ts View File

@ -66,7 +66,7 @@ import {
CopyRemoteResourceCommandQuickPickItem, CopyRemoteResourceCommandQuickPickItem,
OpenRemoteResourceCommandQuickPickItem, OpenRemoteResourceCommandQuickPickItem,
} from '../quickpicks/remoteProviderPicker'; } from '../quickpicks/remoteProviderPicker';
import { isPaidSubscriptionPlan } from '../subscription';
import { isSubscriptionPaidPlan, isSubscriptionPreviewTrialExpired } from '../subscription';
import { filterMap, intersection, isStringArray } from '../system/array'; import { filterMap, intersection, isStringArray } from '../system/array';
import { formatPath } from '../system/formatPath'; import { formatPath } from '../system/formatPath';
import { map } from '../system/iterable'; import { map } from '../system/iterable';
@ -2244,14 +2244,21 @@ export async function* ensureAccessStep<
} else { } else {
if (access.subscription.required == null) return undefined; if (access.subscription.required == null) return undefined;
if (isPaidSubscriptionPlan(access.subscription.required)) {
if (isSubscriptionPaidPlan(access.subscription.required) && access.subscription.current.account != null) {
directives.push(DirectiveQuickPickItem.create(Directive.RequiresPaidSubscription, true)); directives.push(DirectiveQuickPickItem.create(Directive.RequiresPaidSubscription, true));
placeholder = 'Premium features require an upgraded account'; placeholder = 'Premium features require an upgraded account';
} else { } else {
directives.push(
DirectiveQuickPickItem.create(Directive.StartPreview, true),
DirectiveQuickPickItem.create(Directive.RequiresFreeSubscription),
);
if (
access.subscription.current.account == null &&
!isSubscriptionPreviewTrialExpired(access.subscription.current)
) {
directives.push(
DirectiveQuickPickItem.create(Directive.StartPreviewTrial, true),
DirectiveQuickPickItem.create(Directive.RequiresFreeSubscription),
);
} else {
directives.push(DirectiveQuickPickItem.create(Directive.RequiresFreeSubscription));
}
placeholder = 'Premium features require an account'; placeholder = 'Premium features require an account';
} }
} }

src/commands/getStarted.ts → src/commands/walkthroughs.ts View File

@ -22,12 +22,13 @@ export class GetStartedCommand extends Command {
} }
@command() @command()
export class LearnAboutPremiumCommand extends Command {
export class OpenWalkthroughCommand extends Command {
constructor(private readonly container: Container) { constructor(private readonly container: Container) {
super(Commands.LearnAboutPremium);
super(Commands.OpenWalkthrough);
} }
execute() {
void openWalkthrough(this.container.context.extension.id, 'gitlens.premium');
execute(walkthroughAndStep?: string) {
const [walkthroughId, stepId] = walkthroughAndStep?.split('|') ?? 'gitlens.welcome';
void openWalkthrough(this.container.context.extension.id, walkthroughId, stepId);
} }
} }

+ 14
- 2
src/constants.ts View File

@ -100,7 +100,6 @@ export const enum Commands {
FetchRepositories = 'gitlens.fetchRepositories', FetchRepositories = 'gitlens.fetchRepositories',
GetStarted = 'gitlens.getStarted', GetStarted = 'gitlens.getStarted',
InviteToLiveShare = 'gitlens.inviteToLiveShare', InviteToLiveShare = 'gitlens.inviteToLiveShare',
LearnAboutPremium = 'gitlens.learnAboutPremium',
OpenBlamePriorToChange = 'gitlens.openBlamePriorToChange', OpenBlamePriorToChange = 'gitlens.openBlamePriorToChange',
OpenBranchesOnRemote = 'gitlens.openBranchesOnRemote', OpenBranchesOnRemote = 'gitlens.openBranchesOnRemote',
OpenBranchOnRemote = 'gitlens.openBranchOnRemote', OpenBranchOnRemote = 'gitlens.openBranchOnRemote',
@ -122,6 +121,7 @@ export const enum Commands {
OpenRevisionFile = 'gitlens.openRevisionFile', OpenRevisionFile = 'gitlens.openRevisionFile',
OpenRevisionFileInDiffLeft = 'gitlens.openRevisionFileInDiffLeft', OpenRevisionFileInDiffLeft = 'gitlens.openRevisionFileInDiffLeft',
OpenRevisionFileInDiffRight = 'gitlens.openRevisionFileInDiffRight', OpenRevisionFileInDiffRight = 'gitlens.openRevisionFileInDiffRight',
OpenWalkthrough = 'gitlens.openWalkthrough',
OpenWorkingFile = 'gitlens.openWorkingFile', OpenWorkingFile = 'gitlens.openWorkingFile',
OpenWorkingFileInDiffLeft = 'gitlens.openWorkingFileInDiffLeft', OpenWorkingFileInDiffLeft = 'gitlens.openWorkingFileInDiffLeft',
OpenWorkingFileInDiffRight = 'gitlens.openWorkingFileInDiffRight', OpenWorkingFileInDiffRight = 'gitlens.openWorkingFileInDiffRight',
@ -137,6 +137,16 @@ export const enum Commands {
GitCommandsSwitch = 'gitlens.gitCommands.switch', GitCommandsSwitch = 'gitlens.gitCommands.switch',
GitCommandsTag = 'gitlens.gitCommands.tag', GitCommandsTag = 'gitlens.gitCommands.tag',
GitCommandsWorktree = 'gitlens.gitCommands.worktree', GitCommandsWorktree = 'gitlens.gitCommands.worktree',
PremiumLearn = 'gitlens.premium.learn',
PremiumLogin = 'gitlens.premium.login',
PremiumLoginOrSignUp = 'gitlens.premium.loginOrSignUp',
PremiumLogout = 'gitlens.premium.logout',
PremiumPurchase = 'gitlens.premium.purchase',
PremiumResendVerification = 'gitlens.premium.resendVerification',
PremiumShowPlans = 'gitlens.premium.showPlans',
PremiumSignUp = 'gitlens.premium.signUp',
PremiumStartPreviewTrial = 'gitlens.premium.startPreviewTrial',
PremiumValidate = 'gitlens.premium.validate',
QuickOpenFileHistory = 'gitlens.quickOpenFileHistory', QuickOpenFileHistory = 'gitlens.quickOpenFileHistory',
RefreshHover = 'gitlens.refreshHover', RefreshHover = 'gitlens.refreshHover',
ResetAvatarCache = 'gitlens.resetAvatarCache', ResetAvatarCache = 'gitlens.resetAvatarCache',
@ -243,8 +253,10 @@ export const enum ContextKeys {
Premium = 'gitlens:premium', Premium = 'gitlens:premium',
PremiumPaid = 'gitlens:premium:paid', PremiumPaid = 'gitlens:premium:paid',
PremiumRequired = 'gitlens:premium:required',
PremiumRequiresVerification = 'gitlens:premium:requiresVerification', PremiumRequiresVerification = 'gitlens:premium:requiresVerification',
PremiumUpgradeRequired = 'gitlens:premium:upgradeRequired',
PremiumTrial = 'gitlens:premium:trial',
PremiumPreviewTrialExpired = 'gitlens:premium:previewTrial:expired',
} }
export const enum CoreCommands { export const enum CoreCommands {

+ 2
- 2
src/errors.ts View File

@ -1,5 +1,5 @@
import { Uri } from 'vscode'; import { Uri } from 'vscode';
import { isPaidSubscriptionPlan, RequiredSubscriptionPlans, Subscription } from './subscription';
import { isSubscriptionPaidPlan, RequiredSubscriptionPlans, Subscription } from './subscription';
export class AccessDeniedError extends Error { export class AccessDeniedError extends Error {
public readonly subscription: Subscription; public readonly subscription: Subscription;
@ -9,7 +9,7 @@ export class AccessDeniedError extends Error {
let message; let message;
if (subscription.account?.verified === false) { if (subscription.account?.verified === false) {
message = 'Email verification required'; message = 'Email verification required';
} else if (required != null && isPaidSubscriptionPlan(required)) {
} else if (required != null && isSubscriptionPaidPlan(required)) {
message = 'Paid subscription required'; message = 'Paid subscription required';
} else { } else {
message = 'Subscription required'; message = 'Subscription required';

+ 2
- 2
src/git/gitProviderService.ts View File

@ -28,7 +28,7 @@ import { WorkspaceStorageKeys } from '../storage';
import { import {
FreeSubscriptionPlans, FreeSubscriptionPlans,
getSubscriptionPlanPriority, getSubscriptionPlanPriority,
isPaidSubscriptionPlan,
isSubscriptionPaidPlan,
RequiredSubscriptionPlans, RequiredSubscriptionPlans,
Subscription, Subscription,
SubscriptionPlanId, SubscriptionPlanId,
@ -559,7 +559,7 @@ export class GitProviderService implements Disposable {
} }
const plan = subscription.plan.effective.id; const plan = subscription.plan.effective.id;
if (isPaidSubscriptionPlan(plan) || GitProviderService.previewFeatures?.get(feature)) {
if (isSubscriptionPaidPlan(plan) || GitProviderService.previewFeatures?.get(feature)) {
return { allowed: true, subscription: { current: subscription } }; return { allowed: true, subscription: { current: subscription } };
} }

+ 39
- 27
src/premium/subscription/subscriptionService.ts View File

@ -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.PremiumUpgradeRequired,
ContextKeys.PremiumRequired,
allowed allowed
? false ? false
: subscription.required != null && isPaidSubscriptionPlan(subscription.required)
: subscription.required != null && isSubscriptionPaidPlan(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 {

+ 2
- 2
src/quickpicks/items/directive.ts View File

@ -10,7 +10,7 @@ export enum Directive {
RequiresFreeSubscription, RequiresFreeSubscription,
RequiresPaidSubscription, RequiresPaidSubscription,
StartPreview,
StartPreviewTrial,
} }
export namespace Directive { export namespace Directive {
@ -45,7 +45,7 @@ export namespace DirectiveQuickPickItem {
case Directive.Noop: case Directive.Noop:
label = 'Try again'; label = 'Try again';
break; break;
case Directive.StartPreview:
case Directive.StartPreviewTrial:
label = 'Try Premium Features Now'; label = 'Try Premium Features Now';
detail = 'Try premium features for free, without an account, for 3 days'; detail = 'Try premium features for free, without an account, for 3 days';
break; break;

+ 12
- 7
src/subscription.ts View File

@ -18,7 +18,7 @@ export interface Subscription {
readonly effective: SubscriptionPlan; readonly effective: SubscriptionPlan;
}; };
account: SubscriptionAccount | undefined; account: SubscriptionAccount | undefined;
preview?: SubscriptionPreview;
previewTrial?: SubscriptionPreviewTrial;
state: SubscriptionState; state: SubscriptionState;
} }
@ -37,7 +37,7 @@ export interface SubscriptionAccount {
readonly verified: boolean; readonly verified: boolean;
} }
export interface SubscriptionPreview {
export interface SubscriptionPreviewTrial {
readonly startedOn: string; readonly startedOn: string;
readonly expiresOn: string; readonly expiresOn: string;
} }
@ -45,11 +45,11 @@ export interface SubscriptionPreview {
export const enum SubscriptionState { export const enum SubscriptionState {
/** Indicates a user who hasn't verified their email address yet */ /** Indicates a user who hasn't verified their email address yet */
VerificationRequired = -1, VerificationRequired = -1,
/** Indicates a Free user who hasn't yet started the preview */
/** Indicates a Free user who hasn't yet started the preview trial */
Free = 0, Free = 0,
/** Indicates a Free user who is in preview */
/** Indicates a Free user who is in preview trial */
FreeInPreview, FreeInPreview,
/** Indicates a Free user who's preview has expired */
/** Indicates a Free user who's preview has expired trial */
FreePreviewExpired, FreePreviewExpired,
/** Indicates a Free+ user with a completed trial */ /** Indicates a Free+ user with a completed trial */
FreePlusInTrial, FreePlusInTrial,
@ -63,7 +63,7 @@ export function computeSubscriptionState(subscription: Optional
const { const {
account, account,
plan: { actual, effective }, plan: { actual, effective },
preview,
previewTrial: preview,
} = subscription; } = subscription;
if (account?.verified === false) return SubscriptionState.VerificationRequired; if (account?.verified === false) return SubscriptionState.VerificationRequired;
@ -152,7 +152,7 @@ export function getTimeRemaining(
): number | undefined { ): number | undefined {
return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit) : undefined; return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit) : undefined;
} }
export function isPaidSubscriptionPlan(id: SubscriptionPlanId): id is PaidSubscriptionPlans {
export function isSubscriptionPaidPlan(id: SubscriptionPlanId): id is PaidSubscriptionPlans {
return id !== SubscriptionPlanId.Free && id !== SubscriptionPlanId.FreePlus; return id !== SubscriptionPlanId.Free && id !== SubscriptionPlanId.FreePlus;
} }
@ -164,3 +164,8 @@ export function isSubscriptionExpired(subscription: Optional
export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean { export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean {
return subscription.plan.actual.id !== subscription.plan.effective.id; return subscription.plan.actual.id !== subscription.plan.effective.id;
} }
export function isSubscriptionPreviewTrialExpired(subscription: Optional<Subscription, 'state'>): boolean | undefined {
const remaining = getTimeRemaining(subscription.previewTrial?.expiresOn);
return remaining != null ? remaining <= 0 : undefined;
}

+ 22
- 20
src/webviews/apps/premium/home/home.html View File

@ -76,7 +76,7 @@
<a href="command:gitlens.getStarted" appearance="secondary">Walkthrough</a> <a href="command:gitlens.getStarted" appearance="secondary">Walkthrough</a>
</li> </li>
<li> <li>
<a href="command:gitlens.learnAboutPremium" appearance="secondary"
<a href="command:gitlens.premium.learn" appearance="secondary"
>Premium features walkthrough</a >Premium features walkthrough</a
> >
</li> </li>
@ -143,11 +143,11 @@
<h3>Try GitLens Premium Features</h3> <h3>Try GitLens Premium Features</h3>
<p> <p>
Premium features like Premium features like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
> >
are available with a free account, with many more features coming soon, including a commit graph and are available with a free account, with many more features coming soon, including a commit graph and
@ -158,8 +158,10 @@
You can try these premium features for free, without an account, for 3 days. All non-premium features You can try these premium features for free, without an account, for 3 days. All non-premium features
will continue to be free without an account. will continue to be free without an account.
</p> </p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.startPreview">Try premium features now</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.startPreviewTrial"
>Try premium features now</vscode-button
>
<vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button> <vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button>
<span class="button-subaction" <span class="button-subaction"
>Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span >Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span
@ -175,11 +177,11 @@
<h3>Trying Premium Features</h3> <h3>Trying Premium Features</h3>
<p> <p>
You are currently trying premium GitLens features, like You are currently trying premium GitLens features, like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
>, for <span data-bind="previewDays">3 more days</span>. >, for <span data-bind="previewDays">3 more days</span>.
</p> </p>
@ -187,7 +189,7 @@
After that time, a free account will be required to continue using these premium features for public After that time, a free account will be required to continue using these premium features for public
code, or you can puchase a paid plan to access premium features for private repos. code, or you can puchase a paid plan to access premium features for private repos.
</p> </p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button> <vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button>
<span class="button-subaction" <span class="button-subaction"
>Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span >Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span
@ -202,17 +204,17 @@
<h3>Continue using Premium Features</h3> <h3>Continue using Premium Features</h3>
<p> <p>
Premium GitLens features like Premium GitLens features like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
>, a commit graph (coming soon), and GitHub Enterprise integration (coming soon) are only available with >, a commit graph (coming soon), and GitHub Enterprise integration (coming soon) are only available with
an account. an account.
</p> </p>
<p>Create a free account to continue trialing premium features for all code for an additional 7 days.</p> <p>Create a free account to continue trialing premium features for all code for an additional 7 days.</p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button> <vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button>
<span class="button-subaction" <span class="button-subaction"
>Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span >Have an existing account? <a href="command:gitlens.premium.loginOrSignUp">Sign in</a></span
@ -227,17 +229,17 @@
<h3>Premium Feature Trial</h3> <h3>Premium Feature Trial</h3>
<p> <p>
You are currently trialing premium GitLens features like You are currently trialing premium GitLens features like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
> >
for both public and private repos. In <span data-bind="trialDays">7 days</span>, accessing these premium for both public and private repos. In <span data-bind="trialDays">7 days</span>, accessing these premium
features for private repos will require a paid account. features for private repos will require a paid account.
</p> </p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button> <vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
<p> <p>
With your free account, you will continue to have access to premium features for public repos, as well With your free account, you will continue to have access to premium features for public repos, as well
@ -263,18 +265,18 @@
<h3>GitLens Free+</h3> <h3>GitLens Free+</h3>
<p> <p>
With your free account, you have access to GitLens Free+, which unlocks premium features like With your free account, you have access to GitLens Free+, which unlocks premium features like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
> >
for public repos. More premium features like a commit graph and GitHub Enterprise integration are coming for public repos. More premium features like a commit graph and GitHub Enterprise integration are coming
soon. soon.
</p> </p>
<p>Access to premium features for private repos requires a paid plan.</p> <p>Access to premium features for private repos requires a paid plan.</p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button> <vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
</section> </section>
</template> </template>
@ -285,17 +287,17 @@
<p> <p>
Thank you for purchasing <span data-bind="plan">GitLens Pro</span>! With a Thank you for purchasing <span data-bind="plan">GitLens Pro</span>! With a
<span data-bind="plan">GitLens Pro</span> account, you can access premium features like <span data-bind="plan">GitLens Pro</span> account, you can access premium features like
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.worktrees%22"
>Git Worktrees</a >Git Worktrees</a
> >
and and
<a href="command:gitlens.home.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
<a href="command:gitlens.openWalkthrough?%22gitlens.premium%7Cgitlens.premium.visualFileHistory%22"
>Visual File History</a >Visual File History</a
> >
for all of your code. for all of your code.
</p> </p>
<p>Additional premium featues like a commit graph and GitHub Enterprise integration are coming soon.</p> <p>Additional premium featues like a commit graph and GitHub Enterprise integration are coming soon.</p>
<vscode-button data-action="command:gitlens.learnAboutPremium">Learn about premium features</vscode-button>
<vscode-button data-action="command:gitlens.premium.learn">Learn about premium features</vscode-button>
</section> </section>
</template> </template>
</html> </html>

+ 0
- 5
src/webviews/premium/home/homeWebviewView.ts View File

@ -3,7 +3,6 @@ import type { Container } from '../../../container';
import type { SubscriptionChangeEvent } from '../../../premium/subscription/subscriptionService'; import type { SubscriptionChangeEvent } from '../../../premium/subscription/subscriptionService';
import { SyncedStorageKeys } from '../../../storage'; import { SyncedStorageKeys } from '../../../storage';
import type { Subscription } from '../../../subscription'; import type { Subscription } from '../../../subscription';
import { openWalkthrough } from '../../../system/utils';
import { WebviewViewBase } from '../../webviewViewBase'; import { WebviewViewBase } from '../../webviewViewBase';
import { DidChangeSubscriptionNotificationType, State } from './protocol'; import { DidChangeSubscriptionNotificationType, State } from './protocol';
@ -51,10 +50,6 @@ export class HomeWebviewView extends WebviewViewBase {
const subscription = await this.container.subscription.getSubscription(); const subscription = await this.container.subscription.getSubscription();
void this.notifyDidChangeData(subscription); void this.notifyDidChangeData(subscription);
}), }),
commands.registerCommand('gitlens.home.openWalkthrough', (idOrIdPlusStepId: string) => {
const [walkthroughId, stepId] = idOrIdPlusStepId.split('|');
void openWalkthrough(this.container.context.extension.id, walkthroughId, stepId);
}),
]; ];
} }

Loading…
Cancel
Save