Skip to content

Commit

Permalink
Ungates GitLens+ features for local & public repos
Browse files Browse the repository at this point in the history
  • Loading branch information
eamodio committed Sep 12, 2022
1 parent 599c9b7 commit bca9be0
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 92 deletions.
86 changes: 19 additions & 67 deletions src/git/gitProviderService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,14 @@ import { ContextKeys, CoreGitConfiguration, GlyphChars, Schemes } from '../const
import type { Container } from '../container';
import { setContext } from '../context';
import { AccessDeniedError, ProviderNotFoundError } from '../errors';
import type { FeatureAccess, Features } from '../features';
import { PlusFeatures } from '../features';
import type { FeatureAccess, Features, PlusFeatures } from '../features';
import type { RemoteProvider } from '../git/remotes/remoteProvider';
import { Logger } from '../logger';
import type { SubscriptionChangeEvent } from '../plus/subscription/subscriptionService';
import type { RepoComparisonKey } from '../repositories';
import { asRepoComparisonKey, Repositories } from '../repositories';
import type { FreeSubscriptionPlans, RequiredSubscriptionPlans, Subscription } from '../subscription';
import {
getSubscriptionPlan,
getSubscriptionPlanPriority,
isSubscriptionPaidPlan,
SubscriptionPlanId,
} from '../subscription';
import type { RequiredSubscriptionPlans, Subscription } from '../subscription';
import { getSubscriptionPlanPriority, isSubscriptionPaidPlan, SubscriptionPlanId } from '../subscription';
import { groupByFilterMap, groupByMap } from '../system/array';
import { gate } from '../system/decorators/gate';
import { debug, getLogScope, log } from '../system/decorators/log';
Expand Down Expand Up @@ -111,8 +105,6 @@ export const enum RepositoriesVisibility {
}

export class GitProviderService implements Disposable {
static readonly previewFeatures: Map<PlusFeatures | undefined, boolean> | undefined; // = new Map();

private readonly _onDidChangeProviders = new EventEmitter<GitProvidersChangeEvent>();
get onDidChangeProviders(): Event<GitProvidersChangeEvent> {
return this._onDidChangeProviders.event;
Expand Down Expand Up @@ -526,40 +518,10 @@ export class GitProviderService implements Disposable {
cacheKey = path;
}

let accessPromise = this._accessCache.get(cacheKey);
if (accessPromise == null) {
accessPromise = this.accessCore(feature, repoPath);
this._accessCache.set(cacheKey, accessPromise);
}

const access = await accessPromise;
if (feature === PlusFeatures.Graph) {
if (access.visibility == null && repoPath != null) {
access.visibility = await this.visibility(repoPath);
}

if (
(access.visibility !== RepositoryVisibility.Private &&
access.subscription.current.plan.effective.id === SubscriptionPlanId.Free) ||
(access.visibility === RepositoryVisibility.Private && access.subscription.current.previewTrial == null)
) {
return {
allowed: !(
access.visibility === RepositoryVisibility.Private &&
access.subscription.current.previewTrial == null
),
subscription: {
current: {
...access.subscription.current,
plan: {
...access.subscription.current.plan,
effective: getSubscriptionPlan(SubscriptionPlanId.Pro, undefined),
},
},
},
visibility: access.visibility,
};
}
let access = this._accessCache.get(cacheKey);
if (access == null) {
access = this.accessCore(feature, repoPath);
this._accessCache.set(cacheKey, access);
}

return access;
Expand All @@ -573,41 +535,31 @@ export class GitProviderService implements Disposable {
}

const plan = subscription.plan.effective.id;
if (isSubscriptionPaidPlan(plan) || GitProviderService.previewFeatures?.get(feature)) {
if (isSubscriptionPaidPlan(plan)) {
return { allowed: true, subscription: { current: subscription } };
}

function getRepoAccess(
this: GitProviderService,
repoPath: string | Uri,
plan: FreeSubscriptionPlans,
force: boolean = false,
): Promise<FeatureAccess> {
const { path: cacheKey } = this.getProvider(repoPath);

let access = force ? undefined : this._accessCache.get(cacheKey);
if (access == null) {
access = this.visibility(repoPath).then(visibility => {
if (visibility !== RepositoryVisibility.Private) {
switch (plan) {
case SubscriptionPlanId.Free:
return {
allowed: false,
subscription: { current: subscription, required: SubscriptionPlanId.FreePlus },
visibility: visibility,
};
case SubscriptionPlanId.FreePlus:
return {
allowed: true,
subscription: { current: subscription },
visibility: visibility,
};
}
if (visibility === RepositoryVisibility.Private) {
return {
allowed: false,
subscription: { current: subscription, required: SubscriptionPlanId.Pro },
visibility: visibility,
};
}

return {
allowed: false,
subscription: { current: subscription, required: SubscriptionPlanId.Pro },
allowed: true,
subscription: { current: subscription },
visibility: visibility,
};
});
Expand All @@ -625,7 +577,7 @@ export class GitProviderService implements Disposable {
}

if (repositories.length === 1) {
return getRepoAccess.call(this, repositories[0].path, plan);
return getRepoAccess.call(this, repositories[0].path);
}

let allowed = true;
Expand All @@ -634,7 +586,7 @@ export class GitProviderService implements Disposable {

const maxPriority = getSubscriptionPlanPriority(SubscriptionPlanId.Pro);

for await (const result of fastestSettled(repositories.map(r => getRepoAccess.call(this, r.path, plan)))) {
for await (const result of fastestSettled(repositories.map(r => getRepoAccess.call(this, r.path)))) {
if (result.status !== 'fulfilled' || result.value.allowed) continue;

allowed = false;
Expand All @@ -653,7 +605,7 @@ export class GitProviderService implements Disposable {
}

// Pass force = true to bypass the cache and avoid a promise loop (where we used the cached promise we just created to try to resolve itself 🤦)
return getRepoAccess.call(this, repoPath, plan, true);
return getRepoAccess.call(this, repoPath, true);
}

async ensureAccess(feature: PlusFeatures, repoPath?: string): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion src/git/remotes/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export class GitHubRemote extends RichRemoteProvider {
const startTrial = { title: 'Try GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to try GitLens+ free for 3 days?`,
`${title}\n\nDo you want to try GitLens+ for free for 3 days?`,
{ modal: true },
startTrial,
cancel,
Expand Down
2 changes: 1 addition & 1 deletion src/git/remotes/gitlab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class GitLabRemote extends RichRemoteProvider {
const startTrial = { title: 'Try GitLens+' };
const cancel = { title: 'Cancel', isCloseAffordance: true };
const result = await window.showWarningMessage(
`${title}\n\nDo you want to try GitLens+ free for 3 days?`,
`${title}\n\nDo you want to try GitLens+ for free for 3 days?`,
{ modal: true },
startTrial,
cancel,
Expand Down
10 changes: 5 additions & 5 deletions src/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export const enum SubscriptionState {
/** Indicates a Free user who hasn't yet started the preview trial */
Free = 0,
/** Indicates a Free user who is in preview trial */
FreeInPreview,
FreeInPreviewTrial,
/** Indicates a Free user who's preview has expired trial */
FreePreviewExpired,
FreePreviewTrialExpired,
/** Indicates a Free+ user with a completed trial */
FreePlusInTrial,
/** Indicates a Free+ user who's trial has expired */
Expand All @@ -72,7 +72,7 @@ export function computeSubscriptionState(subscription: Optional<Subscription, 's
if (actual.id === effective.id) {
switch (effective.id) {
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreePreviewExpired;
return preview == null ? SubscriptionState.Free : SubscriptionState.FreePreviewTrialExpired;

case SubscriptionPlanId.FreePlus:
return SubscriptionState.FreePlusTrialExpired;
Expand All @@ -86,14 +86,14 @@ export function computeSubscriptionState(subscription: Optional<Subscription, 's

switch (effective.id) {
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreeInPreview;
return preview == null ? SubscriptionState.Free : SubscriptionState.FreeInPreviewTrial;

case SubscriptionPlanId.FreePlus:
return SubscriptionState.FreePlusTrialExpired;

case SubscriptionPlanId.Pro:
return actual.id === SubscriptionPlanId.Free
? SubscriptionState.FreeInPreview
? SubscriptionState.FreeInPreviewTrial
: SubscriptionState.FreePlusInTrial;

case SubscriptionPlanId.Teams:
Expand Down
4 changes: 2 additions & 2 deletions src/views/worktreesView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,11 @@ export class WorktreesView extends ViewBase<WorktreesViewNode, WorktreesViewConf

switch (subscription.state) {
case SubscriptionState.Free:
case SubscriptionState.FreePreviewExpired:
case SubscriptionState.FreePreviewTrialExpired:
case SubscriptionState.VerificationRequired:
this.description = '✨ GitLens+ feature';
break;
case SubscriptionState.FreeInPreview: {
case SubscriptionState.FreeInPreviewTrial: {
const days = getSubscriptionTimeRemaining(subscription, 'days')!;
this.description = `✨⏳ ${pluralize('more day', days)} to try worktrees on public and private repos`;
break;
Expand Down
4 changes: 2 additions & 2 deletions src/webviews/apps/home/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
<%= require('html-loader?{"esModule":false}!./partials/views.html') %>
<%= require('html-loader?{"esModule":false}!./partials/links.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview-trial.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview-trial-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.plus-trial.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.plus-trial-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.paid.html') %>
Expand Down
8 changes: 4 additions & 4 deletions src/webviews/apps/home/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ export class HomeApp extends App<State> {
DOM.insertTemplate('state:free', this.$slots[index++]);

break;
case SubscriptionState.FreeInPreview: {
case SubscriptionState.FreeInPreviewTrial: {
if (viewsVisible) {
DOM.insertTemplate('views', this.$slots[index++]);
}

const remaining = getSubscriptionTimeRemaining(subscription, 'days') ?? 0;
DOM.insertTemplate('state:free-preview', this.$slots[index++], {
DOM.insertTemplate('state:free-preview-trial', this.$slots[index++], {
bindings: {
previewDays: `${
remaining < 1
Expand All @@ -113,12 +113,12 @@ export class HomeApp extends App<State> {

break;
}
case SubscriptionState.FreePreviewExpired:
case SubscriptionState.FreePreviewTrialExpired:
if (viewsVisible) {
DOM.insertTemplate('views', this.$slots[index++]);
}

DOM.insertTemplate('state:free-preview-expired', this.$slots[index++]);
DOM.insertTemplate('state:free-preview-trial-expired', this.$slots[index++]);

break;
case SubscriptionState.FreePlusInTrial: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<template id="state:free-preview-expired">
<template id="state:free-preview-trial-expired">
<h3>Continue using GitLens+ Features</h3>
<p>
Your trial of
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<template id="state:free-preview">
<template id="state:free-preview-trial">
<h3>Trying GitLens+ Features</h3>
<p>
You have <span data-bind="previewDays">3 days</span> left in your
Expand Down
14 changes: 10 additions & 4 deletions src/webviews/apps/plus/graph/GraphWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@ export function GraphWrapper({
const renderTrialDays = () => {
if (
!subscriptionSnapshot ||
![SubscriptionState.FreeInPreview, SubscriptionState.FreePlusInTrial].includes(subscriptionSnapshot.state)
![SubscriptionState.FreeInPreviewTrial, SubscriptionState.FreePlusInTrial].includes(
subscriptionSnapshot.state,
)
) {
return;
}
Expand All @@ -280,15 +282,19 @@ export function GraphWrapper({
let content;
let actions;
let days = 0;
if ([SubscriptionState.FreeInPreview, SubscriptionState.FreePlusInTrial].includes(subscriptionSnapshot.state)) {
if (
[SubscriptionState.FreeInPreviewTrial, SubscriptionState.FreePlusInTrial].includes(
subscriptionSnapshot.state,
)
) {
days = getSubscriptionTimeRemaining(subscriptionSnapshot, 'days') ?? 0;
}

switch (subscriptionSnapshot.state) {
case SubscriptionState.Free:
case SubscriptionState.Paid:
return;
case SubscriptionState.FreeInPreview:
case SubscriptionState.FreeInPreviewTrial:
case SubscriptionState.FreePlusInTrial:
icon = 'calendar';
modifier = 'neutral';
Expand All @@ -306,7 +312,7 @@ export function GraphWrapper({
</>
);
break;
case SubscriptionState.FreePreviewExpired:
case SubscriptionState.FreePreviewTrialExpired:
icon = 'warning';
modifier = 'warning';
content = (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<template id="state:free-preview-expired">
<template id="state:free-preview-trial-expired">
<section>
<p>
Sign in to use the Visual File History and other GitLens+ features on public repos and get an additional
Expand Down
2 changes: 1 addition & 1 deletion src/webviews/apps/plus/timeline/timeline.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ <h1 data-bind="empty"></h1>

<!-- prettier-ignore -->
<%= require('html-loader?{"esModule":false}!./partials/state.free.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.free-preview-trial-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.plus-trial-expired.html') %>
<%= require('html-loader?{"esModule":false}!./partials/state.verify-email.html') %>
</html>
4 changes: 2 additions & 2 deletions src/webviews/apps/plus/timeline/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export class TimelineApp extends App<State> {
case SubscriptionState.Free:
DOM.insertTemplate('state:free', $slot, options);
break;
case SubscriptionState.FreePreviewExpired:
DOM.insertTemplate('state:free-preview-expired', $slot, options);
case SubscriptionState.FreePreviewTrialExpired:
DOM.insertTemplate('state:free-preview-trial-expired', $slot, options);
break;
case SubscriptionState.FreePlusTrialExpired:
DOM.insertTemplate('state:plus-trial-expired', $slot, options);
Expand Down

0 comments on commit bca9be0

Please sign in to comment.