import { getDateDifference } from './system/date'; export const enum SubscriptionPlanId { Free = 'free', FreePlus = 'free+', Pro = 'pro', Teams = 'teams', Enterprise = 'enterprise', } export type FreeSubscriptionPlans = Extract; export type PaidSubscriptionPlans = Exclude; export type RequiredSubscriptionPlans = Exclude; 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): 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([ [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, 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): 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): boolean { const remaining = getSubscriptionTimeRemaining(subscription); return remaining != null && remaining <= 0; } export function isSubscriptionTrial(subscription: Optional): boolean { return subscription.plan.actual.id !== subscription.plan.effective.id; } export function isSubscriptionPreviewTrialExpired(subscription: Optional): boolean | undefined { const remaining = getTimeRemaining(subscription.previewTrial?.expiresOn); return remaining != null ? remaining <= 0 : undefined; }