Skip to content

Commit

Permalink
Improves trials and messaging
Browse files Browse the repository at this point in the history
 - Adds incremental feature preview system (graph only)
- Extends trial period from 7 to 14 days
- Improves clarity around Community vs Pro features

Co-authored-by: Sergio <[email protected]>
  • Loading branch information
eamodio and sergiolms committed Nov 14, 2024
1 parent 38c5a80 commit 3d4b046
Show file tree
Hide file tree
Showing 37 changed files with 860 additions and 314 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ GitLens isn’t just for solo developers—it’s designed to enhance team colla

## Cloud Patches `Preview`

Easily and securely share code changes by creating a Cloud Patch from your work-in-progress, commit, or stash, and sharing a link with teammates or other developers. Cloud Patches enable early collaboration for feedback on direction and approach, reducing rework and streamlining your workflow. [Learn more](https://gitkraken.com/solutions/cloud-patches?utm_source=gitlens-extension&utm_medium=in-app-links)
Privately and securely share code changes by creating a Cloud Patch from your work-in-progress, commit, or stash, and sharing a link with specific teammates and other developers. Cloud Patches enable early collaboration for feedback on direction and approach, reducing rework and streamlining your workflow, without adding noise to your repositories. [Learn more](https://gitkraken.com/solutions/cloud-patches?utm_source=gitlens-extension&utm_medium=in-app-links)

## Code Suggest `Preview`

Expand Down Expand Up @@ -192,7 +192,7 @@ An x-ray or developer tools Inspect into your code, focused on providing context
Quick access to many GitLens features. Also the home of GitKraken teams and collaboration services (e.g. Cloud Patches, Cloud Workspaces), help, and support.

- **Home** &mdash; Quick access to many features.
- [**Cloud Patches `Preview`**](#cloud-patches-preview) &mdash; Easily and securely share code with your teammates
- [**Cloud Patches `Preview`**](#cloud-patches-preview) &mdash; Privately and securely share code with specific teammates
- [**Cloud Workspaces `Preview`**](#gitkraken-workspaces-preview) &mdash; Easily group and manage multiple repositories together, accessible from anywhere, streamlining your workflow.

### Source Control
Expand Down Expand Up @@ -282,7 +282,7 @@ Use the `Generate Commit Message` command from the Source Control view's context

When you're ready to unlock the full potential of GitLens and enjoy all the benefits, consider [upgrading to GitLens Pro](https://gitkraken.dev/register?product=gitlens&source=marketing_page&redirect_uri=vscode%3A%2F%2Feamodio.gitlens%2Flogin&flow=gitlens_web). With GitLens Pro, you'll gain access to [Pro features](https://gitkraken.com/gitlens/pro-features?utm_source=gitlens-extension&utm_medium=in-app-links) on privately-hosted repos.

To learn more about the additional features offered with Pro, visit the [GitLens Community vs GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/?utm_source=gitlens-extension&utm_medium=in-app-links&utm_campaign=readme&utm_term=ready-for-gitlens-pro) page.
To learn more about the additional features offered with Pro, visit the [GitLens Community vs GitLens Pro](https://help.gitkraken.com/gitlens/gitlens-community-vs-gitlens-pro/?utm_source=gitlens-extension&utm_medium=in-app-links&utm_campaign=readme&utm_term=ready-for-gitlens-pro) page.

# Support and Community

Expand Down
125 changes: 59 additions & 66 deletions package.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/@types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export declare global {
export type Mutable<T> = { -readonly [P in keyof T]: T[P] };
export type PickMutable<T, K extends keyof T> = Omit<T, K> & { -readonly [P in K]: T[P] };

export type EntriesType<T> = T extends Record<infer K, infer V> ? [K, V] : never;

export type ExcludeSome<T, K extends keyof T, R> = Omit<T, K> & { [P in K]-?: Exclude<T[P], R> };

export type ExtractAll<T, U> = { [K in keyof T]: T[K] extends U ? T[K] : never };
Expand Down
4 changes: 2 additions & 2 deletions src/commands/quickCommand.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2648,9 +2648,9 @@ export async function* ensureAccessStep<
const promo = getApplicablePromo(access.subscription.current.state, 'gate');
const detail = promo?.quickpick.detail;

placeholder = 'Pro feature — requires a trial or paid plan for use on privately-hosted repos';
placeholder = 'Pro feature — requires a trial or GitLens Pro for use on privately-hosted repos';
if (isSubscriptionPaidPlan(access.subscription.required) && access.subscription.current.account != null) {
placeholder = 'Pro feature — requires a paid plan for use on privately-hosted repos';
placeholder = 'Pro feature — requires GitLens Pro for use on privately-hosted repos';
directives.push(
createDirectiveQuickPickItem(Directive.RequiresPaidSubscription, true, { detail: detail }),
createQuickPickSeparator(),
Expand Down
38 changes: 38 additions & 0 deletions src/commands/resets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ const resetTypes = [
'ai',
'avatars',
'integrations',
'previews',
'repositoryAccess',
'subscription',
'suppressedWarnings',
'usageTracking',
'workspace',
Expand Down Expand Up @@ -73,6 +75,22 @@ export class ResetCommand extends Command {
},
];

if (DEBUG) {
items.push(
createQuickPickSeparator('DEBUG'),
{
label: 'Reset Subscription...',
detail: 'Resets the stored subscription',
item: 'subscription',
},
{
label: 'Reset Feature Previews...',
detail: 'Resets the stored state for feature previews',
item: 'previews',
},
);
}

// create a quick pick with options to clear all the different resets that GitLens supports
const pick = await window.showQuickPick<ResetQuickPickItem>(items, {
title: 'Reset Stored Data',
Expand Down Expand Up @@ -102,10 +120,18 @@ export class ResetCommand extends Command {
confirmationMessage = 'Are you sure you want to reset all of the stored integrations?';
confirm.title = 'Reset Integrations';
break;
case 'previews':
confirmationMessage = 'Are you sure you want to reset the stored state for feature previews?';
confirm.title = 'Reset Feature Previews';
break;
case 'repositoryAccess':
confirmationMessage = 'Are you sure you want to reset the repository access cache?';
confirm.title = 'Reset Repository Access';
break;
case 'subscription':
confirmationMessage = 'Are you sure you want to reset the stored subscription?';
confirm.title = 'Reset Subscription';
break;
case 'suppressedWarnings':
confirmationMessage = 'Are you sure you want to reset all of the suppressed warnings?';
confirm.title = 'Reset Suppressed Warnings';
Expand Down Expand Up @@ -170,6 +196,18 @@ export class ResetCommand extends Command {
case 'workspace':
await this.container.storage.resetWorkspace();
break;
default:
if (DEBUG) {
switch (reset) {
case 'subscription':
await this.container.storage.delete('premium:subscription');
break;
case 'previews':
await this.container.storage.deleteWithPrefix('plus:preview');
break;
}
}
break;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/constants.commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export const enum Commands {
PlusShowPlans = 'gitlens.plus.showPlans',
PlusSignUp = 'gitlens.plus.signUp',
PlusStartPreviewTrial = 'gitlens.plus.startPreviewTrial',
PlusContinueFeaturePreview = 'gitlens.plus.continueFeaturePreview',
PlusUpgrade = 'gitlens.plus.upgrade',
PlusValidate = 'gitlens.plus.validate',
PlusSimulateSubscription = 'gitlens.plus.simulateSubscription',
Expand Down
2 changes: 1 addition & 1 deletion src/constants.context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type ContextKeys = {
'gitlens:newInstall': boolean;
/** Indicates that this is a new install of GitLens (anywhere for this user -- if synced settings is on) */
'gitlens:newUserInstall': boolean;
'gitlens:plus': SubscriptionPlanId;
'gitlens:plus': Exclude<SubscriptionPlanId, SubscriptionPlanId.Community>;
'gitlens:plus:disallowedRepos': string[];
'gitlens:plus:enabled': boolean;
'gitlens:plus:required': boolean;
Expand Down
10 changes: 9 additions & 1 deletion src/constants.storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { IntegrationId } from './constants.integrations';
import type { TrackedUsage, TrackedUsageKeys } from './constants.telemetry';
import type { GroupableTreeViewTypes } from './constants.views';
import type { Environment } from './container';
import type { FeaturePreviews } from './features';
import type { Subscription } from './plus/gk/account/subscription';
import type { Integration } from './plus/integrations/integration';
import type { DeepLinkServiceState } from './uris/deepLinks/deepLink';
Expand Down Expand Up @@ -77,7 +78,9 @@ export type GlobalStorage = {
'launchpadView:groups:expanded': StoredLaunchpadGroup[];
'graph:searchMode': StoredGraphSearchMode;
'views:scm:grouped:welcome:dismissed': boolean;
} & { [key in `confirm:ai:tos:${AIProviders}`]: boolean } & {
} & { [key in `plus:preview:${FeaturePreviews}:usages`]: StoredFeaturePreviewUsagePeriod[] } & {
[key in `confirm:ai:tos:${AIProviders}`]: boolean;
} & {
[key in `provider:authentication:skip:${string}`]: boolean;
} & { [key in `gk:${string}:checkin`]: Stored<StoredGKCheckInResponse> } & {
[key in `gk:${string}:organizations`]: Stored<StoredOrganization[]>;
Expand Down Expand Up @@ -309,3 +312,8 @@ export type StoredLaunchpadGroup =
| 'draft'
| 'other'
| 'snoozed';

export interface StoredFeaturePreviewUsagePeriod {
startedOn: string;
expiresOn: string;
}
6 changes: 4 additions & 2 deletions src/constants.subscription.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const proPreviewLengthInDays = 3;
export const proTrialLengthInDays = 7;
export const proFeaturePreviewUsages = 3;
export const proFeaturePreviewUsageDurationInDays = 1;
export const proPreviewLengthInDays = 0;
export const proTrialLengthInDays = 14;

export type PromoKeys = 'gitlens16' | 'pro50';

Expand Down
11 changes: 10 additions & 1 deletion src/constants.telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Commands } from './constants.commands';
import type { IntegrationId, SupportedCloudIntegrationIds } from './constants.integrations';
import type { SubscriptionState } from './constants.subscription';
import type { CustomEditorTypes, TreeViewTypes, WebviewTypes, WebviewViewTypes } from './constants.views';
import type { FeaturePreviews } from './features';
import type { GitContributionTiers } from './git/models/contributor';
import type { StartWorkType } from './plus/startWork/startWork';
import type { Period } from './plus/webviews/timeline/protocol';
Expand Down Expand Up @@ -418,7 +419,8 @@ export type TelemetryEvents = {
| {
action: 'visibility';
visible: boolean;
};
}
| FeaturePreviewActionEventData;
/** Sent when the subscription changes */
'subscription/changed': SubscriptionEventData;

Expand Down Expand Up @@ -698,3 +700,10 @@ export type TrackedUsageFeatures =
| `${TreeViewTypes | WebviewViewTypes}View`
| `${CustomEditorTypes}Editor`;
export type TrackedUsageKeys = `${TrackedUsageFeatures}:shown` | CommandExecutionTrackedFeatures | WalkthroughUsageKeys;

export type FeaturePreviewActionsDayEventData = Record<`day.${number}.startedOn`, string>;
export type FeaturePreviewActionEventData = {
action: `start-preview-trial:${FeaturePreviews}`;
startedOn: string;
day: number;
} & FeaturePreviewActionsDayEventData;
2 changes: 1 addition & 1 deletion src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class AccessDeniedError extends Error {
if (subscription.account?.verified === false) {
message = 'Email verification required';
} else if (required != null && isSubscriptionPaidPlan(required)) {
message = 'Paid plan required';
message = 'GitLens Pro required';
} else {
message = 'Plan required';
}
Expand Down
35 changes: 35 additions & 0 deletions src/features.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { StoredFeaturePreviewUsagePeriod } from './constants.storage';
import { proFeaturePreviewUsages } from './constants.subscription';
import type { RepositoryVisibility } from './git/gitProvider';
import type { RequiredSubscriptionPlans, Subscription } from './plus/gk/account/subscription';
import { capitalize } from './system/string';

export const enum Features {
Stashes = 'stashes',
Expand Down Expand Up @@ -39,3 +42,35 @@ export const enum PlusFeatures {
Graph = 'graph',
Launchpad = 'launchpad',
}

export type FeaturePreviews = 'graph';
export const featurePreviews: FeaturePreviews[] = ['graph'];

export interface FeaturePreview {
feature: FeaturePreviews;
usages: StoredFeaturePreviewUsagePeriod[];
}

export function getFeaturePreviewLabel(feature: FeaturePreviews) {
switch (feature) {
case 'graph':
return 'Commit Graph';
default:
return capitalize(feature);
}
}

export function isFeaturePreviewActive(featurePreview?: FeaturePreview) {
const usages = featurePreview?.usages;
if (usages == null || usages.length === 0) return false;

return usages.length <= proFeaturePreviewUsages && new Date(usages[usages.length - 1].expiresOn) > new Date();
}

export function isFeaturePreviewExpired(featurePreview: FeaturePreview) {
const usages = featurePreview.usages;
return (
usages.length > proFeaturePreviewUsages ||
(usages.length === proFeaturePreviewUsages && new Date(usages[usages.length - 1].expiresOn) < new Date())
);
}
Loading

0 comments on commit 3d4b046

Please sign in to comment.