Browse Source

Updates verbiage for GL+ and GL Pro

- updates plan names
- consolidates ensurePaidPlan functionality
- updates account quickpicks
- updates account notification messages

Co-authored-by: Eric Amodio <eamodio@gmail.com>
main
Keith Daulton 2 years ago
parent
commit
c90b1c36ef
8 changed files with 120 additions and 187 deletions
  1. +1
    -1
      src/commands/gitCommands.ts
  2. +8
    -14
      src/commands/quickCommand.steps.ts
  3. +2
    -73
      src/git/remotes/github.ts
  4. +2
    -73
      src/git/remotes/gitlab.ts
  5. +78
    -0
      src/git/remotes/richRemoteProvider.ts
  6. +15
    -11
      src/plus/subscription/subscriptionService.ts
  7. +10
    -11
      src/quickpicks/items/directive.ts
  8. +4
    -4
      src/subscription.ts

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

@ -712,7 +712,7 @@ export class GitCommandsCommand extends Command {
resolve(undefined); resolve(undefined);
return; return;
case Directive.RequiresFreeSubscription:
case Directive.ExtendTrial:
void Container.instance.subscription.loginOrSignUp(); void Container.instance.subscription.loginOrSignUp();
resolve(undefined); resolve(undefined);
return; return;

+ 8
- 14
src/commands/quickCommand.steps.ts View File

@ -2167,26 +2167,20 @@ export async function* ensureAccessStep<
let placeholder: string; let placeholder: string;
if (access.subscription.current.account?.verified === false) { if (access.subscription.current.account?.verified === false) {
directives.push(DirectiveQuickPickItem.create(Directive.RequiresVerification, true)); directives.push(DirectiveQuickPickItem.create(Directive.RequiresVerification, true));
placeholder = 'You must verify your GitLens+ account email address before you can continue';
placeholder = 'You must verify your email address before you can continue';
} else { } else {
if (access.subscription.required == null) return undefined; if (access.subscription.required == null) return undefined;
placeholder = 'You need GitLens Pro to access GitLens+ features on this repo';
if (isSubscriptionPaidPlan(access.subscription.required) && access.subscription.current.account != null) { 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 = 'GitLens+ features require an upgraded account';
} else if (
access.subscription.current.account == null &&
!isSubscriptionPreviewTrialExpired(access.subscription.current)
) {
directives.push(DirectiveQuickPickItem.create(Directive.StartPreviewTrial, true));
} else { } else {
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 = 'GitLens+ features require a free account';
directives.push(DirectiveQuickPickItem.create(Directive.ExtendTrial));
} }
} }

+ 2
- 73
src/git/remotes/github.ts View File

@ -7,7 +7,6 @@ import type {
IntegrationAuthenticationProvider, IntegrationAuthenticationProvider,
IntegrationAuthenticationSessionDescriptor, IntegrationAuthenticationSessionDescriptor,
} from '../../plus/integrationAuthentication'; } from '../../plus/integrationAuthentication';
import { isSubscriptionPaidPlan, isSubscriptionPreviewTrialExpired } from '../../subscription';
import { log } from '../../system/decorators/log'; import { log } from '../../system/decorators/log';
import { memoize } from '../../system/decorators/memoize'; import { memoize } from '../../system/decorators/memoize';
import { encodeUrl } from '../../system/encoding'; import { encodeUrl } from '../../system/encoding';
@ -18,7 +17,7 @@ import type { IssueOrPullRequest } from '../models/issue';
import type { PullRequest, PullRequestState } from '../models/pullRequest'; import type { PullRequest, PullRequestState } from '../models/pullRequest';
import { GitRevision } from '../models/reference'; import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository'; import type { Repository } from '../models/repository';
import { RichRemoteProvider } from './richRemoteProvider';
import { ensurePaidPlan, RichRemoteProvider } from './richRemoteProvider';
const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g; const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g;
const fileRegex = /^\/([^/]+)\/([^/]+?)\/blob(.+)$/i; const fileRegex = /^\/([^/]+)\/([^/]+?)\/blob(.+)$/i;
@ -141,77 +140,7 @@ export class GitHubRemote extends RichRemoteProvider {
@log() @log()
override async connect(): Promise<boolean> { override async connect(): Promise<boolean> {
if (!isGitHubDotCom(this.domain)) { if (!isGitHubDotCom(this.domain)) {
const title =
'Connecting to a GitHub Enterprise instance for rich integration features requires a paid GitLens+ account.';
while (true) {
const subscription = await this.container.subscription.getSubscription();
if (subscription.account?.verified === false) {
const resend = { title: 'Resend Verification' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nYou must verify your GitLens+ account email address before you can continue.`,
{ modal: true },
resend,
cancel,
);
if (result === resend) {
if (await this.container.subscription.resendVerification()) {
continue;
}
}
return false;
}
const plan = subscription.plan.effective.id;
if (isSubscriptionPaidPlan(plan)) break;
if (subscription.account == null && !isSubscriptionPreviewTrialExpired(subscription)) {
const startTrial = { title: 'Try GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to try GitLens+ for free for 3 days?`,
{ modal: true },
startTrial,
cancel,
);
if (result !== startTrial) return false;
void this.container.subscription.startPreviewTrial();
break;
} else if (subscription.account == null) {
const signIn = { title: 'Sign In to GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to sign in to GitLens+?`,
{ modal: true },
signIn,
cancel,
);
if (result === signIn) {
if (await this.container.subscription.loginOrSignUp()) {
continue;
}
}
} else {
const upgrade = { title: 'Upgrade Account' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to upgrade your account?`,
{ modal: true },
upgrade,
cancel,
);
if (result === upgrade) {
void this.container.subscription.purchase();
}
}
if (!(await ensurePaidPlan('GitHub Enterprise instance', this.container))) {
return false; return false;
} }
} }

+ 2
- 73
src/git/remotes/gitlab.ts View File

@ -8,7 +8,6 @@ import type {
IntegrationAuthenticationProvider, IntegrationAuthenticationProvider,
IntegrationAuthenticationSessionDescriptor, IntegrationAuthenticationSessionDescriptor,
} from '../../plus/integrationAuthentication'; } from '../../plus/integrationAuthentication';
import { isSubscriptionPaidPlan, isSubscriptionPreviewTrialExpired } from '../../subscription';
import { log } from '../../system/decorators/log'; import { log } from '../../system/decorators/log';
import { encodeUrl } from '../../system/encoding'; import { encodeUrl } from '../../system/encoding';
import { equalsIgnoreCase } from '../../system/string'; import { equalsIgnoreCase } from '../../system/string';
@ -18,7 +17,7 @@ import type { IssueOrPullRequest } from '../models/issue';
import type { PullRequest, PullRequestState } from '../models/pullRequest'; import type { PullRequest, PullRequestState } from '../models/pullRequest';
import { GitRevision } from '../models/reference'; import { GitRevision } from '../models/reference';
import type { Repository } from '../models/repository'; import type { Repository } from '../models/repository';
import { RichRemoteProvider } from './richRemoteProvider';
import { ensurePaidPlan, RichRemoteProvider } from './richRemoteProvider';
const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g; const autolinkFullIssuesRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)#(?<num>[0-9]+)\b(?!]\()/g;
const autolinkFullMergeRequestsRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)!(?<num>[0-9]+)\b(?!]\()/g; const autolinkFullMergeRequestsRegex = /\b(?<repo>[^/\s]+\/[^/\s]+)!(?<num>[0-9]+)\b(?!]\()/g;
@ -184,77 +183,7 @@ export class GitLabRemote extends RichRemoteProvider {
@log() @log()
override async connect(): Promise<boolean> { override async connect(): Promise<boolean> {
if (!equalsIgnoreCase(this.domain, 'gitlab.com')) { if (!equalsIgnoreCase(this.domain, 'gitlab.com')) {
const title =
'Connecting to a GitLab self-managed instance for rich integration features requires a paid GitLens+ account.';
while (true) {
const subscription = await this.container.subscription.getSubscription();
if (subscription.account?.verified === false) {
const resend = { title: 'Resend Verification' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nYou must verify your GitLens+ account email address before you can continue.`,
{ modal: true },
resend,
cancel,
);
if (result === resend) {
if (await this.container.subscription.resendVerification()) {
continue;
}
}
return false;
}
const plan = subscription.plan.effective.id;
if (isSubscriptionPaidPlan(plan)) break;
if (subscription.account == null && !isSubscriptionPreviewTrialExpired(subscription)) {
const startTrial = { title: 'Try GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to try GitLens+ for free for 3 days?`,
{ modal: true },
startTrial,
cancel,
);
if (result !== startTrial) return false;
void this.container.subscription.startPreviewTrial();
break;
} else if (subscription.account == null) {
const signIn = { title: 'Sign In to GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to sign in to GitLens+?`,
{ modal: true },
signIn,
cancel,
);
if (result === signIn) {
if (await this.container.subscription.loginOrSignUp()) {
continue;
}
}
} else {
const upgrade = { title: 'Upgrade Account' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to upgrade your account?`,
{ modal: true },
upgrade,
cancel,
);
if (result === upgrade) {
void this.container.subscription.purchase();
}
}
if (!(await ensurePaidPlan('GitLab self-managed instance', this.container))) {
return false; return false;
} }
} }

+ 78
- 0
src/git/remotes/richRemoteProvider.ts View File

@ -8,6 +8,7 @@ import { AuthenticationError, ProviderRequestClientError } from '../../errors';
import { Logger } from '../../logger'; import { Logger } from '../../logger';
import { showIntegrationDisconnectedTooManyFailedRequestsWarningMessage } from '../../messages'; import { showIntegrationDisconnectedTooManyFailedRequestsWarningMessage } from '../../messages';
import type { IntegrationAuthenticationSessionDescriptor } from '../../plus/integrationAuthentication'; import type { IntegrationAuthenticationSessionDescriptor } from '../../plus/integrationAuthentication';
import { isSubscriptionPaidPlan, isSubscriptionPreviewTrialExpired } from '../../subscription';
import { gate } from '../../system/decorators/gate'; import { gate } from '../../system/decorators/gate';
import { debug, getLogScope, log } from '../../system/decorators/log'; import { debug, getLogScope, log } from '../../system/decorators/log';
import { isPromise } from '../../system/promise'; import { isPromise } from '../../system/promise';
@ -466,3 +467,80 @@ export abstract class RichRemoteProvider extends RemoteProvider {
return session ?? undefined; return session ?? undefined;
} }
} }
export async function ensurePaidPlan(providerName: string, container: Container): Promise<boolean> {
const title = `Connecting to a ${providerName} instance for rich integration features requires GitLens Pro.`;
while (true) {
const subscription = await container.subscription.getSubscription();
if (subscription.account?.verified === false) {
const resend = { title: 'Resend Verification' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nYou must verify your email address before you can continue.`,
{ modal: true },
resend,
cancel,
);
if (result === resend) {
if (await container.subscription.resendVerification()) {
continue;
}
}
return false;
}
const plan = subscription.plan.effective.id;
if (isSubscriptionPaidPlan(plan)) break;
if (subscription.account == null && !isSubscriptionPreviewTrialExpired(subscription)) {
const startTrial = { title: 'Start a GitLens Pro Trial' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to also try GitLens+ features on private repos, free for 3 days?`,
{ modal: true },
startTrial,
cancel,
);
if (result !== startTrial) return false;
void container.subscription.startPreviewTrial();
break;
} else if (subscription.account == null) {
const signIn = { title: 'Extend Your GitLens Pro Trial' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to continue to use GitLens+ features on private repos, free for an additional 7-days?`,
{ modal: true },
signIn,
cancel,
);
if (result === signIn) {
if (await container.subscription.loginOrSignUp()) {
continue;
}
}
} else {
const upgrade = { title: 'Upgrade to Pro' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to continue to use GitLens+ features on private repos?`,
{ modal: true },
upgrade,
cancel,
);
if (result === upgrade) {
void container.subscription.purchase();
}
}
return false;
}
return true;
}

+ 15
- 11
src/plus/subscription/subscriptionService.ts View File

@ -242,7 +242,7 @@ export class SubscriptionService implements Disposable {
const confirm: MessageItem = { title: 'Resend Verification', isCloseAffordance: true }; const confirm: MessageItem = { title: 'Resend Verification', isCloseAffordance: true };
const cancel: MessageItem = { title: 'Cancel' }; const cancel: MessageItem = { title: 'Cancel' };
const result = await window.showInformationMessage( const result = await window.showInformationMessage(
`Before you can access your ${actual.name} account, you must verify your email address.`,
`Before you can access ${effective.name}, you must verify your email address.`,
confirm, confirm,
cancel, cancel,
); );
@ -256,11 +256,12 @@ export class SubscriptionService implements Disposable {
const confirm: MessageItem = { title: 'OK', isCloseAffordance: true }; const confirm: MessageItem = { title: 'OK', isCloseAffordance: true };
const learn: MessageItem = { title: 'Learn More' }; const learn: MessageItem = { title: 'Learn More' };
const result = await window.showInformationMessage( const result = await window.showInformationMessage(
`You are now signed in to your ${
actual.name
} account which gives you access to GitLens+ features on public repos.\n\nYou were also granted a trial of ${
`Welcome to ${
effective.name effective.name
} for both public and private repos for ${pluralize('more day', remaining ?? 0)}.`,
} (Trial). You now have additional access to GitLens+ features on private repos for ${pluralize(
'more day',
remaining ?? 0,
)}.`,
{ modal: true }, { modal: true },
confirm, confirm,
learn, learn,
@ -270,7 +271,10 @@ export class SubscriptionService implements Disposable {
this.learn(); this.learn();
} }
} else { } else {
void window.showInformationMessage(`You are now signed in to your ${actual.name} account.`, 'OK');
void window.showInformationMessage(
`Welcome to ${actual.name}. You now have additional access to GitLens+ features on private repos.`,
'OK',
);
} }
} }
return loggedIn; return loggedIn;
@ -427,10 +431,10 @@ export class SubscriptionService implements Disposable {
void this.showHomeView(); void this.showHomeView();
if (!silent && plan.effective.id === SubscriptionPlanId.Free) { if (!silent && plan.effective.id === SubscriptionPlanId.Free) {
const confirm: MessageItem = { title: 'Sign in to GitLens+', isCloseAffordance: true };
const confirm: MessageItem = { title: 'Extend Your Trial', isCloseAffordance: true };
const cancel: MessageItem = { title: 'Cancel' }; const cancel: MessageItem = { title: 'Cancel' };
const result = await window.showInformationMessage( const result = await window.showInformationMessage(
'Your GitLens+ features trial has ended.\nPlease sign in to use GitLens+ features on public repos and get a free 7-day trial for both public and private repos.',
'Your 3-day trial has ended.\nExtend your GitLens Pro trial to continue to use GitLens+ features on private repos, free for an additional 7-days.',
{ modal: true }, { modal: true },
confirm, confirm,
cancel, cancel,
@ -476,7 +480,7 @@ export class SubscriptionService implements Disposable {
const confirm: MessageItem = { title: 'OK', isCloseAffordance: true }; const confirm: MessageItem = { title: 'OK', isCloseAffordance: true };
const learn: MessageItem = { title: 'Learn More' }; const learn: MessageItem = { title: 'Learn More' };
const result = await window.showInformationMessage( const result = await window.showInformationMessage(
`You have started a ${days} day trial of GitLens+ features for both public and private repos.`,
`You have started a ${days}-day GitLens Pro trial of GitLens+ features on private repos.`,
{ modal: true }, { modal: true },
confirm, confirm,
learn, learn,
@ -920,8 +924,8 @@ export class SubscriptionService implements Disposable {
this._statusBarSubscription.backgroundColor = new ThemeColor('statusBarItem.warningBackground'); this._statusBarSubscription.backgroundColor = new ThemeColor('statusBarItem.warningBackground');
this._statusBarSubscription.tooltip = new MarkdownString( this._statusBarSubscription.tooltip = new MarkdownString(
trial trial
? `**Please verify your email**\n\nBefore you can start your **${effective.name}** trial, please verify the email for the account you created.\n\nClick for details`
: `**Please verify your email**\n\nBefore you can use GitLens+ features, please verify the email for the account you created.\n\nClick for details`,
? `**Please verify your email**\n\nBefore you can start your **${effective.name}** trial, please verify your email address.\n\nClick for details`
: `**Please verify your email**\n\nBefore you can also use GitLens+ features on private repos, please verify your email address.\n\nClick for details`,
true, true,
); );
} else { } else {

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

@ -8,7 +8,7 @@ export enum Directive {
Noop, Noop,
RequiresVerification, RequiresVerification,
RequiresFreeSubscription,
ExtendTrial,
RequiresPaidSubscription, RequiresPaidSubscription,
StartPreviewTrial, StartPreviewTrial,
} }
@ -46,21 +46,20 @@ export namespace DirectiveQuickPickItem {
label = 'Try again'; label = 'Try again';
break; break;
case Directive.StartPreviewTrial: case Directive.StartPreviewTrial:
label = 'Try GitLens+ Features Now';
detail = 'Try GitLens+ features now, without an account, for 3 days';
label = 'Start a GitLens Pro Trial';
detail = 'Try GitLens+ features on private repos, free for 3 days, without an account';
break;
case Directive.ExtendTrial:
label = 'Extend Your GitLens Pro Trial';
detail = 'To continue to use GitLens+ features on private repos, free for an additional 7-days';
break; break;
case Directive.RequiresVerification: case Directive.RequiresVerification:
label = 'Resend Verification Email'; label = 'Resend Verification Email';
detail = 'You must verify your GitLens+ account email address before you can continue';
break;
case Directive.RequiresFreeSubscription:
label = 'Sign in to GitLens+';
detail =
'To use GitLens+ features on public repos and get a free 7-day trial for both public and private repos';
detail = 'You must verify your email address before you can continue';
break; break;
case Directive.RequiresPaidSubscription: case Directive.RequiresPaidSubscription:
label = 'Upgrade your account';
detail = 'To use GitLens+ features on both public and private repos';
label = 'Upgrade to Pro';
detail = 'To use GitLens+ features on private repos';
break; break;
} }
} }

+ 4
- 4
src/subscription.ts View File

@ -114,13 +114,13 @@ export function getSubscriptionPlan(id: SubscriptionPlanId, startedOn?: Date, ex
export function getSubscriptionPlanName(id: SubscriptionPlanId) { export function getSubscriptionPlanName(id: SubscriptionPlanId) {
switch (id) { switch (id) {
case SubscriptionPlanId.FreePlus: case SubscriptionPlanId.FreePlus:
return 'GitLens+';
return 'GitLens';
case SubscriptionPlanId.Pro: case SubscriptionPlanId.Pro:
return 'GitLens+ Pro';
return 'GitLens Pro';
case SubscriptionPlanId.Teams: case SubscriptionPlanId.Teams:
return 'GitLens+ Teams';
return 'GitLens Teams';
case SubscriptionPlanId.Enterprise: case SubscriptionPlanId.Enterprise:
return 'GitLens+ Enterprise';
return 'GitLens Enterprise';
case SubscriptionPlanId.Free: case SubscriptionPlanId.Free:
default: default:
return 'GitLens'; return 'GitLens';

Loading…
Cancel
Save