Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ See the [FAQ](#is-gitlens-free-to-use 'Jump to FAQ') for more details.

[Features](#discover-powerful-features 'Jump to Discover Powerful Features')
| [Labs](#gitkraken-labs 'Jump to GitKraken Labs')
| [Pro](#ready-for-gitlens-pro 'Jump to Ready for GitKraken Pro?')
| [Pro](#ready-for-gitlens-pro 'Jump to Ready for GitLens Pro?')
| [FAQ](#faq 'Jump to FAQ')
| [Support and Community](#support-and-community 'Jump to Support and Community')
| [Contributing](#contributing 'Jump to Contributing')
Expand Down Expand Up @@ -257,11 +257,11 @@ Use the Explain panel on the **Commit Details** view to leverage AI to help you

Use the `Generate Commit Message` command from the Source Control view's context menu to automatically generate a commit message for your staged changes by leveraging AI.

# Ready for GitKraken Pro?
# Ready for GitLens Pro?

When you're ready to unlock the full potential of GitLens and enjoy all the benefits on your privately hosted repos, consider upgrading to GitKraken Pro. With GitKraken Pro, you'll gain access to ✨ features on privately hosted repos and ☁️ features based on the Pro plan.
When you're ready to unlock the full potential of GitLens and enjoy all the benefits on your privately hosted repos, consider upgrading to GitLens Pro. With GitLens Pro, you'll gain access to ✨ features on privately hosted repos and ☁️ features based on the Pro plan.

To learn more about the pricing and the additional ✨ and ☁️ features offered with GitKraken Pro, visit the [GitLens Pricing page](https://www.gitkraken.com/gitlens/pricing). Upgrade to GitKraken Pro today and take your Git workflow to the next level!
To learn more about the pricing and the additional ✨ and ☁️ features offered with GitLens Pro, visit the [GitLens Pricing page](https://www.gitkraken.com/gitlens/pricing). Upgrade to GitLens Pro today and take your Git workflow to the next level!

# FAQ

Expand All @@ -274,7 +274,7 @@ Yes. All features are free to use on all repos, **except** for features,

While GitLens offers a remarkable set of free features, a subset of features tailored for professional developers and teams, marked with a ✨, require a trial or paid plan for use on privately hosted repos — use on local or publicly hosted repos is free for everyone. Additionally some features marked with a ☁️, rely on GitKraken Dev Services which requires a GitKraken account and access is based on your plan, e.g. Free, Pro, etc.

Preview ✨ features instantly for free for 3 days without an account, or start a free GitKraken trial to get an additional 7 days and gain access to ☁️ features to experience the full power of GitLens.
Preview ✨ features instantly for free for 3 days without an account, or start a free GitLens Pro trial to get an additional 7 days and gain access to ☁️ features to experience the full power of GitLens.

## Are ✨ and ☁️ features free to use?

Expand All @@ -300,7 +300,7 @@ Join the GitLens community on [GitHub Discussions](https://github.com/gitkraken/

For any issues or inquiries related to GitLens, you can reach out to the GitKraken support team via the [official support page](https://support.gitkraken.com/). They will be happy to assist you with any problems you may encounter.

With GitKraken Pro, you gain access to priority email support from our customer success team, ensuring higher priority and faster response times. Custom onboarding and training are also available to help you and your team quickly get up and running with a GitKraken Pro plan.
With GitLens Pro, you gain access to priority email support from our customer success team, ensuring higher priority and faster response times. Custom onboarding and training are also available to help you and your team quickly get up and running with a GitLens Pro plan.

# Contributing

Expand Down
30 changes: 22 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5123,6 +5123,11 @@
"title": "Preview Pro",
"category": "GitLens"
},
{
"command": "gitlens.plus.reactivateProTrial",
"title": "Reactivate Pro Trial",
"category": "GitLens"
},
{
"command": "gitlens.plus.manage",
"title": "Manage Your Account...",
Expand Down Expand Up @@ -8929,6 +8934,10 @@
"command": "gitlens.plus.startPreviewTrial",
"when": "!gitlens:plus"
},
{
"command": "gitlens.plus.reactivateProTrial",
"when": "gitlens:plus:state == 5"
},
{
"command": "gitlens.plus.manage",
"when": "gitlens:plus"
Expand Down Expand Up @@ -16123,7 +16132,7 @@
},
{
"view": "gitlens.views.drafts",
"contents": "[Start Free Pro Trial](command:gitlens.plus.signUp)\n\nStart a free 7-day Pro trial to use Cloud Patches, or [sign in](command:gitlens.plus.login).\n☁️ Requires a GitKraken account and access is based on your plan, e.g. Free, Pro, etc",
"contents": "[Start Pro Trial](command:gitlens.plus.signUp)\n\nStart a free 7-day GitLens Pro trial to use Cloud Patches, or [sign in](command:gitlens.plus.login).\n☁️ Requires a GitKraken account and access is based on your plan, e.g. Free, Pro, etc",
"when": "!gitlens:plus"
},
{
Expand All @@ -16137,7 +16146,7 @@
},
{
"view": "gitlens.views.workspaces",
"contents": "[Start Free GitKraken Trial](command:gitlens.plus.signUp)\n\nStart a free 7-day GitKraken trial to use GitKraken Workspaces, or [sign in](command:gitlens.plus.login).\n☁️ Requires a GitKraken account and access is based on your plan, e.g. Free, Pro, etc",
"contents": "[Start Pro Trial](command:gitlens.plus.signUp)\n\nStart a free 7-day GitLens Pro trial to use GitKraken Workspaces, or [sign in](command:gitlens.plus.login).\n☁️ Requires a GitKraken account and access is based on your plan, e.g. Free, Pro, etc",
"when": "!gitlens:plus"
},
{
Expand All @@ -16157,18 +16166,23 @@
},
{
"view": "gitlens.views.worktrees",
"contents": "[Preview Pro](command:gitlens.plus.startPreviewTrial)\n\nPreview Pro for 3 days, or [sign up](command:gitlens.plus.signUp) to start a full 7-day GitKraken trial.\n✨ A trial or paid plan is required to use this on privately hosted repos.",
"contents": "[Preview Pro](command:gitlens.plus.startPreviewTrial)\n\nPreview Pro for 3 days, or [sign up](command:gitlens.plus.signUp) to start a full 7-day GitLens Pro trial.\n[✨ Worktrees](https://help.gitkraken.com/gitlens/side-bar/#worktrees-view%e2%9c%a8) — seamlessly work on multiple branches simultaneously.",
"when": "gitlens:plus:required && gitlens:plus:state == 0"
},
{
"view": "gitlens.views.worktrees",
"contents": "Your 3-day Pro preview has ended, start a free GitKraken trial to get an additional 7 days, or [sign in](command:gitlens.plus.login).\n\n[Start Free GitKraken Trial](command:gitlens.plus.signUp)\n✨ A trial or paid plan is required to use this on privately hosted repos.",
"contents": "Your 3-day preview has ended. Start a free GitLens Pro trial to get an additional 7 days, or [sign in](command:gitlens.plus.login).\n\n[Start Pro Trial](command:gitlens.plus.signUp)\n[✨ Worktrees](https://help.gitkraken.com/gitlens/side-bar/#worktrees-view%e2%9c%a8) — seamlessly work on multiple branches simultaneously.",
"when": "gitlens:plus:required && gitlens:plus:state == 2"
},
{
"view": "gitlens.views.worktrees",
"contents": "Your GitKraken trial has ended, please upgrade to continue to use this on privately hosted repos.\n\n[Upgrade to Pro](command:gitlens.plus.purchase)\n✨ A paid plan is required to use this on privately hosted repos.",
"contents": "Your GitLens Pro trial has ended. Please upgrade to continue to use this on privately hosted repos.\n\n[Get GitLens Pro](command:gitlens.plus.purchase)\n[✨ Worktrees](https://help.gitkraken.com/gitlens/side-bar/#worktrees-view%e2%9c%a8) — seamlessly work on multiple branches simultaneously.",
"when": "gitlens:plus:required && gitlens:plus:state == 4"
},
{
"view": "gitlens.views.worktrees",
"contents": "You're eligible to reactivate your GitLens Pro trial and experience all the new Pro features — free for another 7 days!\n\n[Try Pro](command:gitlens.plus.reactivateProTrial)\n[✨ Worktrees](https://help.gitkraken.com/gitlens/side-bar/#worktrees-view%e2%9c%a8) — seamlessly work on multiple branches simultaneously.",
"when": "gitlens:plus:required && gitlens:plus:state == 5"
}
],
"views": {
Expand Down Expand Up @@ -16441,16 +16455,16 @@
},
{
"id": "gitlens.welcome.preview",
"title": "Previewing GitKraken Pro",
"description": "During your preview, you have access to ✨ features on privately hosted repos. [Learn more](https://www.gitkraken.com/gitlens/pro-features)\n\n[Start Free GitKraken Trial](command:gitlens.plus.signUp)\n\nStart a free GitKraken trial to get an additional 7 days.",
"title": "Previewing GitLens Pro",
"description": "During your preview, you have access to ✨ features on privately hosted repos. [Learn more](https://www.gitkraken.com/gitlens/pro-features)\n\n[Start Free Pro Trial](command:gitlens.plus.signUp)\n\nStart a free Pro trial to get an additional 7 days.",
"media": {
"markdown": "walkthroughs/welcome/preview.md"
},
"when": "gitlens:plus:state == 1"
},
{
"id": "gitlens.welcome.trial",
"title": "Trialing GitKraken Pro",
"title": "Trialing GitLens Pro",
"description": "During your trial, you have access to ✨ features on privately hosted repos and ☁️ features based on the Pro plan. [Learn more](https://www.gitkraken.com/gitlens/pro-features)\n\n[Upgrade to Pro](command:gitlens.plus.purchase)",
"media": {
"markdown": "walkthroughs/welcome/trial.md"
Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export const enum Commands {
PlusLogout = 'gitlens.plus.logout',
PlusManage = 'gitlens.plus.manage',
PlusPurchase = 'gitlens.plus.purchase',
PlusReactivateProTrial = 'gitlens.plus.reactivateProTrial',
PlusResendVerification = 'gitlens.plus.resendVerification',
PlusRestore = 'gitlens.plus.restore',
PlusShowPlans = 'gitlens.plus.showPlans',
Expand Down
25 changes: 21 additions & 4 deletions src/plus/gk/account/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface SubscriptionPlan {
readonly name: string;
readonly bundle: boolean;
readonly trialReactivationCount: number;
readonly nextTrialOptInDate?: string | undefined;
readonly cancelled: boolean;
readonly startedOn: string;
readonly expiresOn?: string | undefined;
Expand All @@ -53,6 +54,7 @@ export interface SubscriptionPreviewTrial {
readonly expiresOn: string;
}

// Note: Pay attention to gitlens:plus:state in package.json when modifying this enum
export const enum SubscriptionState {
/** Indicates a user who hasn't verified their email address yet */
VerificationRequired = -1,
Expand All @@ -64,8 +66,10 @@ export const enum SubscriptionState {
FreePreviewTrialExpired,
/** Indicates a Free+ user with a completed trial */
FreePlusInTrial,
/** Indicates a Free+ user who's trial has expired */
/** Indicates a Free+ user who's trial has expired and is not yet eligible for reactivation */
FreePlusTrialExpired,
/** Indicated a Free+ user who's trial has expired and is eligible for reactivation */
FreePlusTrialReactivationEligible,
/** Indicates a Paid user */
Paid,
}
Expand All @@ -84,8 +88,13 @@ export function computeSubscriptionState(subscription: Optional<Subscription, 's
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreePreviewTrialExpired;

case SubscriptionPlanId.FreePlus:
case SubscriptionPlanId.FreePlus: {
if (effective.nextTrialOptInDate != null && new Date(effective.nextTrialOptInDate) < new Date()) {
return SubscriptionState.FreePlusTrialReactivationEligible;
}

return SubscriptionState.FreePlusTrialExpired;
}

case SubscriptionPlanId.Pro:
case SubscriptionPlanId.Teams:
Expand All @@ -98,8 +107,13 @@ export function computeSubscriptionState(subscription: Optional<Subscription, 's
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreeInPreviewTrial;

case SubscriptionPlanId.FreePlus:
case SubscriptionPlanId.FreePlus: {
if (effective.nextTrialOptInDate != null && new Date(effective.nextTrialOptInDate) < new Date()) {
return SubscriptionState.FreePlusTrialReactivationEligible;
}

return SubscriptionState.FreePlusTrialExpired;
}

case SubscriptionPlanId.Pro:
return actual.id === SubscriptionPlanId.Free
Expand All @@ -120,6 +134,7 @@ export function getSubscriptionPlan(
startedOn?: Date,
expiresOn?: Date,
cancelled: boolean = false,
nextTrialOptInDate?: string,
): SubscriptionPlan {
return {
id: id,
Expand All @@ -128,6 +143,7 @@ export function getSubscriptionPlan(
cancelled: cancelled,
organizationId: organizationId,
trialReactivationCount: trialReactivationCount,
nextTrialOptInDate: nextTrialOptInDate,
startedOn: (startedOn ?? new Date()).toISOString(),
expiresOn: expiresOn != null ? expiresOn.toISOString() : undefined,
};
Expand All @@ -152,6 +168,7 @@ export function getSubscriptionPlanName(id: SubscriptionPlanId) {
export function getSubscriptionStatePlanName(state: SubscriptionState | undefined, id: SubscriptionPlanId | undefined) {
switch (state) {
case SubscriptionState.FreePlusTrialExpired:
case SubscriptionState.FreePlusTrialReactivationEligible:
return getSubscriptionPlanName(SubscriptionPlanId.FreePlus);
case SubscriptionState.FreeInPreviewTrial:
return `${getSubscriptionPlanName(SubscriptionPlanId.Pro)} (Trial)`;
Expand Down Expand Up @@ -193,7 +210,7 @@ export function getTimeRemaining(
expiresOn: string | undefined,
unit?: 'days' | 'hours' | 'minutes' | 'seconds',
): number | undefined {
return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit) : undefined;
return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit, Math.round) : undefined;
}

export function isSubscriptionPaid(subscription: Optional<Subscription, 'state'>): boolean {
Expand Down
68 changes: 65 additions & 3 deletions src/plus/gk/account/subscriptionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ProgressLocation,
StatusBarAlignment,
ThemeColor,
Uri,
window,
} from 'vscode';
import { getPlatform } from '@env/platform';
Expand Down Expand Up @@ -173,6 +174,7 @@ export class SubscriptionService implements Disposable {
registerCommand(Commands.PlusLogout, () => this.logout()),

registerCommand(Commands.PlusStartPreviewTrial, () => this.startPreviewTrial()),
registerCommand(Commands.PlusReactivateProTrial, () => this.reactivateProTrial()),
registerCommand(Commands.PlusManage, () => this.manage()),
registerCommand(Commands.PlusPurchase, () => this.purchase()),

Expand Down Expand Up @@ -436,10 +438,10 @@ export class SubscriptionService implements Disposable {
void this.showAccountView();

if (!silent && plan.effective.id === SubscriptionPlanId.Free) {
const confirm: MessageItem = { title: 'Start Free GitKraken Trial', isCloseAffordance: true };
const confirm: MessageItem = { title: 'Start Pro Trial', isCloseAffordance: true };
const cancel: MessageItem = { title: 'Cancel' };
const result = await window.showInformationMessage(
'Your 3-day Pro preview has ended, start a free GitKraken trial to get an additional 7 days.\n\n✨ A trial or paid plan is required to use Pro features on privately hosted repos.',
'Your 3-day preview has ended. Start a free GitLens Pro trial to get an additional 7 days.\n\n✨ A trial or paid plan is required to use Pro features on privately hosted repos.',
{ modal: true },
confirm,
cancel,
Expand Down Expand Up @@ -492,7 +494,7 @@ export class SubscriptionService implements Disposable {
`You can now preview Pro features for ${pluralize(
'day',
days,
)}. After which, you can start a free GitKraken trial for an additional 7 days.`,
)}. After which, you can start a free GitLens Pro trial for an additional 7 days.`,
confirm,
learn,
);
Expand All @@ -504,6 +506,66 @@ export class SubscriptionService implements Disposable {
}
}

@gate()
@log()
async reactivateProTrial(): Promise<void> {
if (!(await ensurePlusFeaturesEnabled())) return;
const scope = getLogScope();

const session = await this.ensureSession(false);
if (session == null) return;

const rsp = await this.connection.fetchApi('user/reactivate-trial', {
method: 'POST',
body: JSON.stringify({ client: 'gitlens' }),
});

if (!rsp.ok) {
if (rsp.status === 409) {
void window.showErrorMessage(
'Unable to reactivate trial: User not eligible. Please try again. If this issue persists, please contact support.',
'OK',
);
return;
}

void window.showErrorMessage(
`Unable to reactivate trial: (${rsp.status}) ${rsp.statusText}. Please try again. If this issue persists, please contact support.`,
'OK',
);
return;
}

// Trial was reactivated. Do a check-in to update, and show a message if successful.
try {
await this.checkInAndValidate(session, { force: true });
if (isSubscriptionTrial(this._subscription)) {
const remaining = getSubscriptionTimeRemaining(this._subscription, 'days');

const confirm: MessageItem = { title: 'OK', isCloseAffordance: true };
const learn: MessageItem = { title: "See What's New" };
const result = await window.showInformationMessage(
`Your new trial has been activated! Enjoy access to Pro features on privately hosted repos for another ${pluralize(
'day',
remaining ?? 0,
)}.`,
{ modal: true },
confirm,
learn,
);

if (result === learn) {
void env.openExternal(
Uri.parse('https://help.gitkraken.com/gitlens/gitlens-release-notes-current/'),
);
}
}
} catch (ex) {
Logger.error(ex, scope);
debugger;
}
}

@gate<SubscriptionService['validate']>(o => `${o?.force ?? false}`)
@log()
async validate(options?: { force?: boolean }): Promise<void> {
Expand Down
6 changes: 6 additions & 0 deletions src/plus/gk/checkin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface GKCheckInResponse {
readonly paidLicenses: Record<GKLicenseType, GKLicense>;
readonly effectiveLicenses: Record<GKLicenseType, GKLicense>;
};
readonly nextOptInDate?: string;
}

export interface GKUser {
Expand All @@ -25,6 +26,7 @@ export interface GKLicense {
readonly latestEndDate: string;
readonly organizationId: string | undefined;
readonly reactivationCount?: number;
readonly nextOptInDate?: string;
}

export type GKLicenseType =
Expand Down Expand Up @@ -144,6 +146,9 @@ export function getSubscriptionFromCheckIn(
: data.user.createdDate != null
? new Date(data.user.createdDate)
: undefined,
undefined,
undefined,
data.nextOptInDate,
);
}

Expand All @@ -162,6 +167,7 @@ export function getSubscriptionFromCheckIn(
new Date(license.latestStartDate),
new Date(license.latestEndDate),
license.latestStatus === 'cancelled',
license.nextOptInDate ?? data.nextOptInDate,
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/plus/integrations/providerIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ export async function ensurePaidPlan(providerName: string, container: Container)
void container.subscription.startPreviewTrial();
break;
} else if (subscription.account == null) {
const signIn = { title: 'Start Free GitKraken Trial' };
const signIn = { title: 'Start Pro Trial' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to continue to use ✨ features on privately hosted repos, free for an additional 7 days?`,
Expand Down
2 changes: 1 addition & 1 deletion src/plus/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function ensurePaidPlan(title: string, container: Container): Promi
if (isSubscriptionPaidPlan(plan)) break;

if (subscription.account == null) {
const signIn = { title: 'Start Free GitKraken Trial' };
const signIn = { title: 'Start Pro Trial' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nTry our developer productivity and collaboration services free for 7 days.`,
Expand Down
2 changes: 1 addition & 1 deletion src/plus/workspaces/workspacesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export class WorkspacesService implements Disposable {
cloudWorkspaces: cloudWorkspaces,
cloudWorkspaceInfo:
filteredSharedWorkspaceCount > 0
? `${filteredSharedWorkspaceCount} shared workspaces hidden - upgrade to GitKraken Pro to access.`
? `${filteredSharedWorkspaceCount} shared workspaces hidden - upgrade to GitLens Pro to access.`
: undefined,
};
}
Expand Down
Loading