Browse Source

Polishes subscription messaging & flows

main
Eric Amodio 2 years ago
parent
commit
97410679b3
9 changed files with 133 additions and 46 deletions
  1. +16
    -6
      package.json
  2. +14
    -1
      src/commands/gitCommands.ts
  3. +10
    -7
      src/commands/quickCommand.steps.ts
  4. +1
    -0
      src/constants.ts
  5. +13
    -0
      src/env/browser/platform.ts
  6. +13
    -0
      src/env/node/platform.ts
  7. +25
    -3
      src/premium/subscription/subscriptionService.ts
  8. +10
    -4
      src/quickpicks/items/directive.ts
  9. +31
    -25
      src/webviews/apps/premium/home/home.html

+ 16
- 6
package.json View File

@ -3583,7 +3583,7 @@
},
{
"command": "gitlens.premium.purchase",
"title": "Unlock Premium Features for Private Code...",
"title": "Unlock Premium Features for Private Repos...",
"category": "GitLens Premium"
},
{
@ -10464,18 +10464,28 @@
},
{
"view": "gitlens.views.worktrees",
"contents": "Worktrees are a premium feature, which require a free account for public code. [Learn more](https://dev.gitkraken.com/gitlens/premium-features).",
"when": "gitlens:premium:upgradeRequired == free+"
"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",
"contents": "Worktrees are a premium feature, which require at least a Pro subscription for private code. [Learn more](https://dev.gitkraken.com/gitlens/premium-features).",
"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"
},
{
"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"
},
{
"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"
},
{
"view": "gitlens.views.worktrees",
"contents": "[Unlock Premium Features](command:gitlens.showHomeView)",
"when": "gitlens:premium:upgradeRequired"
"contents": "[Upgrade your account](command:gitlens.premium.purchase)",
"when": "gitlens:premium:upgradeRequired == paid"
}
],
"views": {

+ 14
- 1
src/commands/gitCommands.ts View File

@ -709,10 +709,23 @@ export class GitCommandsCommand extends Command {
void loadMore();
return;
case Directive.StartPreview:
void Container.instance.subscription.startPreview();
resolve(undefined);
return;
case Directive.RequiresVerification:
void Container.instance.subscription.resendVerification();
resolve(undefined);
return;
case Directive.RequiresFreeSubscription:
void Container.instance.subscription.loginOrSignUp();
resolve(undefined);
return;
case Directive.RequiresPaidSubscription:
void Container.instance.subscription.showHomeView();
void Container.instance.subscription.purchase();
resolve(undefined);
return;
}

+ 10
- 7
src/commands/quickCommand.steps.ts View File

@ -2236,27 +2236,30 @@ export async function* ensureAccessStep<
const access = await Container.instance.git.access(feature, state.repo.path);
if (access.allowed) return undefined;
let directive: Directive;
const directives: DirectiveQuickPickItem[] = [];
let placeholder: string;
if (access.subscription.current.account?.verified === false) {
directive = Directive.RequiresVerification;
directives.push(DirectiveQuickPickItem.create(Directive.RequiresVerification, true));
placeholder = 'You must verify your account email address before you can continue';
} else {
if (access.subscription.required == null) return undefined;
if (isPaidSubscriptionPlan(access.subscription.required)) {
directive = Directive.RequiresPaidSubscription;
placeholder = 'Requires a paid subscription';
directives.push(DirectiveQuickPickItem.create(Directive.RequiresPaidSubscription, true));
placeholder = 'Premium features require an upgraded account';
} else {
directive = Directive.RequiresFreeSubscription;
placeholder = 'Requires a Free+ account';
directives.push(
DirectiveQuickPickItem.create(Directive.StartPreview, true),
DirectiveQuickPickItem.create(Directive.RequiresFreeSubscription),
);
placeholder = 'Premium features require an account';
}
}
const step = QuickCommand.createPickStep<DirectiveQuickPickItem>({
title: appendReposToTitle(context.title, state, context),
placeholder: placeholder,
items: [DirectiveQuickPickItem.create(directive, true), DirectiveQuickPickItem.create(Directive.Cancel)],
items: [...directives, DirectiveQuickPickItem.create(Directive.Cancel)],
});
const selection: StepSelection<typeof step> = yield step;

+ 1
- 0
src/constants.ts View File

@ -243,6 +243,7 @@ export const enum ContextKeys {
Premium = 'gitlens:premium',
PremiumPaid = 'gitlens:premium:paid',
PremiumRequiresVerification = 'gitlens:premium:requiresVerification',
PremiumUpgradeRequired = 'gitlens:premium:upgradeRequired',
}

+ 13
- 0
src/env/browser/platform.ts View File

@ -6,3 +6,16 @@ const _userAgent = navigator.userAgent;
export const isLinux = _platform === 'Linux' || _userAgent.indexOf('Linux') >= 0;
export const isMac = _platform === 'macOS' || _userAgent.indexOf('Macintosh') >= 0;
export const isWindows = _platform === 'Windows' || _userAgent.indexOf('Windows') >= 0;
export function getPlatform(): string {
if (isWindows) {
return 'web-windows';
}
if (isMac) {
return 'web-macOS';
}
if (isLinux) {
return 'web-linux';
}
return 'web';
}

+ 13
- 0
src/env/node/platform.ts View File

@ -5,3 +5,16 @@ export const isWeb = env.uiKind === UIKind.Web;
export const isLinux = process.platform === 'linux';
export const isMac = process.platform === 'darwin';
export const isWindows = process.platform === 'win32';
export function getPlatform(): string {
if (isWindows) {
return 'windows';
}
if (isMac) {
return 'macOS';
}
if (isLinux) {
return 'linux';
}
return isWeb ? 'web' : 'unknown';
}

+ 25
- 3
src/premium/subscription/subscriptionService.ts View File

@ -1,6 +1,7 @@
import {
authentication,
AuthenticationSession,
version as codeVersion,
commands,
Disposable,
env,
@ -13,6 +14,7 @@ import {
window,
} from 'vscode';
import { fetch } from '@env/fetch';
import { getPlatform } from '@env/platform';
import { Commands, ContextKeys } from '../../constants';
import type { Container } from '../../container';
import { setContext } from '../../context';
@ -157,6 +159,8 @@ export class SubscriptionService implements Disposable {
}
async loginOrSignUp(): Promise<boolean> {
void this.showHomeView();
const session = await this.ensureSession(true);
return Boolean(session);
}
@ -232,10 +236,10 @@ export class SubscriptionService implements Disposable {
let { plan, preview } = this._subscription;
if (preview != null || plan.effective.id !== SubscriptionPlanId.Free) {
if (plan.effective.id === SubscriptionPlanId.Free) {
const ok = { title: 'Create Free+ Account' };
const ok = { title: 'Extend Trial' };
const cancel = { title: 'Cancel' };
const result = await window.showInformationMessage(
'Your premium feature preview has expired. Please create a Free+ account to extend your trial.',
'Your premium feature trial has expired. Please create a free account to extend your trial.',
ok,
cancel,
);
@ -249,13 +253,16 @@ export class SubscriptionService implements Disposable {
const startedOn = new Date();
let days;
let expiresOn = new Date(startedOn);
if (!this.container.debugging && this.container.env !== 'dev') {
// Normalize the date to just before midnight on the same day
expiresOn.setHours(23, 59, 59, 999);
expiresOn = createFromDateDelta(expiresOn, { days: 3 });
days = 3;
} else {
expiresOn = createFromDateDelta(expiresOn, { minutes: 1 });
days = 0;
}
preview = {
@ -271,6 +278,8 @@ export class SubscriptionService implements Disposable {
},
preview: preview,
});
void window.showInformationMessage(`You can now try premium GitLens features for ${days} days.`);
}
async validate(): Promise<void> {
@ -282,12 +291,24 @@ export class SubscriptionService implements Disposable {
private async checkInAndValidate(session: AuthenticationSession): Promise<void> {
try {
const checkInData = {
id: session.account.id,
platform: getPlatform(),
gitlensVersion: this.container.version,
vscodeEdition: env.appName,
vscodeHost: env.appHost,
vscodeVersion: codeVersion,
previewStartedOn: this._subscription.preview?.startedOn,
previewExpiresOn: this._subscription.preview?.expiresOn,
};
const rsp = await fetch(Uri.joinPath(this.baseApiUri, 'gitlens/checkin').toString(), {
method: 'POST',
headers: {
Authorization: `Bearer ${session.accessToken}`,
'User-Agent': userAgent,
},
body: JSON.stringify(checkInData),
});
if (!rsp.ok) {
@ -506,6 +527,7 @@ export class SubscriptionService implements Disposable {
void setContext(ContextKeys.Premium, actual.id);
void setContext(ContextKeys.PremiumPaid, isPaidSubscriptionPlan(actual.id));
void setContext(ContextKeys.PremiumRequiresVerification, this._subscription.account?.verified === false);
}
private updateStatusBar(pending: boolean = false): void {
@ -530,7 +552,7 @@ export class SubscriptionService implements Disposable {
this._statusBarSubscription.text = effective.name;
this._statusBarSubscription.command = Commands.ShowHomeView;
this._statusBarSubscription.tooltip = new MarkdownString(
`You are on **${effective.name}**\n\nClick to upgrade to Free+ for access to premium features for public code`,
`You are on **${effective.name}**\n\nClick to upgrade to Free+ for access to premium features for public repos`,
true,
);
break;

+ 10
- 4
src/quickpicks/items/directive.ts View File

@ -7,8 +7,10 @@ export enum Directive {
LoadMore,
Noop,
RequiresVerification,
RequiresFreeSubscription,
RequiresPaidSubscription,
StartPreview,
}
export namespace Directive {
@ -43,17 +45,21 @@ export namespace DirectiveQuickPickItem {
case Directive.Noop:
label = 'Try again';
break;
case Directive.StartPreview:
label = 'Try Premium Features Now';
detail = 'Try premium features for free, without an account, for 3 days';
break;
case Directive.RequiresVerification:
label = 'Resend Verification Email';
detail = 'You must verify your account email address before you can continue';
break;
case Directive.RequiresFreeSubscription:
label = 'Create a Free+ Account';
detail = 'To unlock all premium features for public code';
label = 'Create a Free Account';
detail = 'To unlock premium features';
break;
case Directive.RequiresPaidSubscription:
label = 'Upgrade to a paid subscription';
detail = 'To unlock all premium features for private code';
label = 'Upgrade Your Account';
detail = 'To unlock premium features for private repos';
break;
}
}

+ 31
- 25
src/webviews/apps/premium/home/home.html View File

@ -52,7 +52,9 @@
individually.
</li>
</ul>
<vscode-button data-action="command:gitlens.home.hideWelcome" appearance="secondary">Hide</vscode-button>
<vscode-button data-action="command:gitlens.home.hideWelcome" appearance="secondary"
>Dismiss welcome</vscode-button
>
</section>
</template>
@ -62,8 +64,11 @@
<p>
Premium features like <a href="">Git Worktrees</a> and <a href="">Visual File History</a> are available
with a free account, with many more features coming soon, including a commit graph and GitHub Enterprise
integration. Access to premium features for private code is available with a paid account.
<a href="">Learn more</a> about GitLens premium features.
integration. Access to premium features for private repos is available with a paid account.
</p>
<p>
<a href="https://dev.gitkraken.com/gitlens/premium-features">Learn more</a> about GitLens premium
features.
</p>
<p>
You can try these premium features for free, without an account, for 3 days. All non-premium features
@ -83,11 +88,14 @@
<p>
You are currently trying premium GitLens features, like <a href="">Git Worktrees</a> and
<a href="">Visual File History</a>, for <span data-bind="previewDays">3 more days</span>.
<a href="">Learn more</a> about GitLens premium features.
</p>
<p>
<a href="https://dev.gitkraken.com/gitlens/premium-features">Learn more</a> about GitLens premium
features.
</p>
<p>
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 code.
code, or you can puchase a paid plan to access premium features for private repos.
</p>
<vscode-button data-action="command:gitlens.premium.loginOrSignUp">Create a free account</vscode-button>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
@ -101,15 +109,16 @@
<p>
Premium GitLens features like <a href="">Git Worktrees</a> and <a href="">Visual File History</a>, a
commit graph (coming soon), and GitHub Enterprise integration (coming soon) are only available with an
account. <a href="">Learn more</a> about GitLens premium features.
account.
</p>
<p>
<a href="https://dev.gitkraken.com/gitlens/premium-features">Learn more</a> about GitLens premium
features.
</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.premium.loginOrSignUp">Create a free account</vscode-button>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
<p>All non-premium features will continue to be free without an account.</p>
<vscode-button data-action="command:gitlens.home.hideSubscription" appearance="secondary"
>Close</vscode-button
>
</section>
</template>
@ -118,14 +127,14 @@
<h3>Premium Feature Trial</h3>
<p>
You are currently trialing premium GitLens features like <a href="">Git Worktrees</a> and
<a href="">Visual File History</a> for both public and private code. In
<span data-bind="trialDays">7 days</span>, accessing these premium features for private code will
<a href="">Visual File History</a> 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.
</p>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
<p>
With your free account, you will continue to have access to premium features for public code, as well as
all non-premium features.
With your free account, you will continue to have access to premium features for public repos, as well
as all non-premium features.
</p>
<!-- <vscode-button appearance="secondary">Close</vscode-button> -->
</section>
@ -133,12 +142,12 @@
<template id="state:verify-email">
<section>
<h3>Please validate your email</h3>
<p>To continue using premium GitLens features, please validate the email for the account you created.</p>
<h3>Please verify your email</h3>
<p>To continue using premium GitLens features, please verify the email for the account you created.</p>
<vscode-button data-action="command:gitlens.premium.resendVerification"
>Resend verification email</vscode-button
>
<vscode-button data-action="command:gitlens.premium.validate">Refresh validation</vscode-button>
<vscode-button data-action="command:gitlens.premium.validate">Refresh verification status</vscode-button>
<p>All non-premium features will continue to be free without an account.</p>
</section>
</template>
@ -148,15 +157,15 @@
<h3>GitLens Free+</h3>
<p>
With your free account, you have access to GitLens Free+, which unlocks premium features like
<a href="">Git Worktrees</a> and <a href="">Visual File History</a> for public code. More premium
<a href="">Git Worktrees</a> and <a href="">Visual File History</a> for public repos. More premium
features like a commit graph and GitHub Enterprise integration are coming soon.
<a href="">Learn more</a> about GitLens premium features.
</p>
<p>Access to premium features for private code requires a paid plan.</p>
<p>
<a href="https://dev.gitkraken.com/gitlens/premium-features">Learn more</a> about GitLens premium
features.
</p>
<p>Access to premium features for private repos requires a paid plan.</p>
<vscode-button data-action="command:gitlens.premium.purchase">Purchase a plan</vscode-button>
<vscode-button data-action="command:gitlens.home.hideSubscription" appearance="secondary"
>Close</vscode-button
>
</section>
</template>
@ -172,9 +181,6 @@
Additional premium featues like a commit graph and GitHub Enterprise integration are coming soon.
<a href="">Learn more</a> about GitLens premium features.
</p>
<vscode-button data-action="command:gitlens.home.hideSubscription" appearance="secondary"
>Close</vscode-button
>
</section>
</template>
</html>

Loading…
Cancel
Save