Skip to content

Commit

Permalink
Polishes subscription messaging & flows
Browse files Browse the repository at this point in the history
  • Loading branch information
eamodio committed Feb 15, 2022
1 parent 5ad4d74 commit 9741067
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 46 deletions.
22 changes: 16 additions & 6 deletions package.json
Expand Up @@ -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"
},
{
Expand Down Expand Up @@ -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": {
Expand Down
15 changes: 14 additions & 1 deletion src/commands/gitCommands.ts
Expand Up @@ -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;
}
Expand Down
17 changes: 10 additions & 7 deletions src/commands/quickCommand.steps.ts
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Expand Up @@ -243,6 +243,7 @@ export const enum ContextKeys {

Premium = 'gitlens:premium',
PremiumPaid = 'gitlens:premium:paid',
PremiumRequiresVerification = 'gitlens:premium:requiresVerification',
PremiumUpgradeRequired = 'gitlens:premium:upgradeRequired',
}

Expand Down
13 changes: 13 additions & 0 deletions src/env/browser/platform.ts
Expand Up @@ -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 changes: 13 additions & 0 deletions src/env/node/platform.ts
Expand Up @@ -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';
}
28 changes: 25 additions & 3 deletions src/premium/subscription/subscriptionService.ts
@@ -1,6 +1,7 @@
import {
authentication,
AuthenticationSession,
version as codeVersion,
commands,
Disposable,
env,
Expand All @@ -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';
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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,
);
Expand All @@ -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 = {
Expand All @@ -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> {
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
14 changes: 10 additions & 4 deletions src/quickpicks/items/directive.ts
Expand Up @@ -7,8 +7,10 @@ export enum Directive {
LoadMore,
Noop,
RequiresVerification,

RequiresFreeSubscription,
RequiresPaidSubscription,
StartPreview,
}

export namespace Directive {
Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 9741067

Please sign in to comment.