You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

176 lines
5.4 KiB

import { getDateDifference } from './system/date';
export const enum SubscriptionPlanId {
Free = 'free',
FreePlus = 'free+',
Pro = 'pro',
Teams = 'teams',
Enterprise = 'enterprise',
}
export type FreeSubscriptionPlans = Extract<SubscriptionPlanId, SubscriptionPlanId.Free | SubscriptionPlanId.FreePlus>;
export type PaidSubscriptionPlans = Exclude<SubscriptionPlanId, SubscriptionPlanId.Free | SubscriptionPlanId.FreePlus>;
export type RequiredSubscriptionPlans = Exclude<SubscriptionPlanId, SubscriptionPlanId.Free>;
export interface Subscription {
readonly plan: {
readonly actual: SubscriptionPlan;
readonly effective: SubscriptionPlan;
};
account: SubscriptionAccount | undefined;
previewTrial?: SubscriptionPreviewTrial;
state: SubscriptionState;
}
export interface SubscriptionPlan {
readonly id: SubscriptionPlanId;
readonly name: string;
readonly startedOn: string;
readonly expiresOn?: string | undefined;
}
export interface SubscriptionAccount {
readonly id: string;
readonly name: string;
readonly email: string | undefined;
readonly verified: boolean;
}
export interface SubscriptionPreviewTrial {
readonly startedOn: string;
readonly expiresOn: string;
}
export const enum SubscriptionState {
/** Indicates a user who hasn't verified their email address yet */
VerificationRequired = -1,
/** Indicates a Free user who hasn't yet started the preview trial */
Free = 0,
/** Indicates a Free user who is in preview trial */
FreeInPreview,
/** Indicates a Free user who's preview has expired trial */
FreePreviewExpired,
/** Indicates a Free+ user with a completed trial */
FreePlusInTrial,
/** Indicates a Free+ user who's trial has expired */
FreePlusTrialExpired,
/** Indicates a Paid user */
Paid,
}
export function computeSubscriptionState(subscription: Optional<Subscription, 'state'>): SubscriptionState {
const {
account,
plan: { actual, effective },
previewTrial: preview,
} = subscription;
if (account?.verified === false) return SubscriptionState.VerificationRequired;
if (actual.id === effective.id) {
switch (effective.id) {
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreePreviewExpired;
case SubscriptionPlanId.FreePlus:
return SubscriptionState.FreePlusTrialExpired;
case SubscriptionPlanId.Pro:
case SubscriptionPlanId.Teams:
case SubscriptionPlanId.Enterprise:
return SubscriptionState.Paid;
}
}
switch (effective.id) {
case SubscriptionPlanId.Free:
return preview == null ? SubscriptionState.Free : SubscriptionState.FreeInPreview;
case SubscriptionPlanId.FreePlus:
return SubscriptionState.FreePlusTrialExpired;
case SubscriptionPlanId.Pro:
return actual.id === SubscriptionPlanId.Free
? SubscriptionState.FreeInPreview
: SubscriptionState.FreePlusInTrial;
case SubscriptionPlanId.Teams:
case SubscriptionPlanId.Enterprise:
return SubscriptionState.Paid;
}
}
export function getSubscriptionPlan(id: SubscriptionPlanId, startedOn?: Date, expiresOn?: Date): SubscriptionPlan {
return {
id: id,
name: getSubscriptionPlanName(id),
startedOn: (startedOn ?? new Date()).toISOString(),
expiresOn: expiresOn != null ? expiresOn.toISOString() : undefined,
};
}
export function getSubscriptionPlanName(id: SubscriptionPlanId) {
switch (id) {
case SubscriptionPlanId.FreePlus:
return 'GitLens Free+';
case SubscriptionPlanId.Pro:
return 'GitLens Pro';
case SubscriptionPlanId.Teams:
return 'GitLens Teams';
case SubscriptionPlanId.Enterprise:
return 'GitLens Enterprise';
case SubscriptionPlanId.Free:
default:
return 'GitLens Free';
}
}
const plansPriority = new Map<SubscriptionPlanId | undefined, number>([
[undefined, -1],
[SubscriptionPlanId.Free, 0],
[SubscriptionPlanId.FreePlus, 1],
[SubscriptionPlanId.Pro, 2],
[SubscriptionPlanId.Teams, 3],
[SubscriptionPlanId.Enterprise, 4],
]);
export function getSubscriptionPlanPriority(id: SubscriptionPlanId | undefined): number {
return plansPriority.get(id)!;
}
export function getSubscriptionTimeRemaining(
subscription: Optional<Subscription, 'state'>,
unit?: 'days' | 'hours' | 'minutes' | 'seconds',
): number | undefined {
return getTimeRemaining(subscription.plan.effective.expiresOn, unit);
}
export function getTimeRemaining(
expiresOn: string | undefined,
unit?: 'days' | 'hours' | 'minutes' | 'seconds',
): number | undefined {
return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit) : undefined;
}
export function isSubscriptionPaid(subscription: Optional<Subscription, 'state'>): boolean {
return isSubscriptionPaidPlan(subscription.plan.effective.id);
}
export function isSubscriptionPaidPlan(id: SubscriptionPlanId): id is PaidSubscriptionPlans {
return id !== SubscriptionPlanId.Free && id !== SubscriptionPlanId.FreePlus;
}
export function isSubscriptionExpired(subscription: Optional<Subscription, 'state'>): boolean {
const remaining = getSubscriptionTimeRemaining(subscription);
return remaining != null && remaining <= 0;
}
export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean {
return subscription.plan.actual.id !== subscription.plan.effective.id;
}
export function isSubscriptionPreviewTrialExpired(subscription: Optional<Subscription, 'state'>): boolean | undefined {
const remaining = getTimeRemaining(subscription.previewTrial?.expiresOn);
return remaining != null ? remaining <= 0 : undefined;
}