Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

176 linhas
5.4 KiB

  1. import { getDateDifference } from './system/date';
  2. export const enum SubscriptionPlanId {
  3. Free = 'free',
  4. FreePlus = 'free+',
  5. Pro = 'pro',
  6. Teams = 'teams',
  7. Enterprise = 'enterprise',
  8. }
  9. export type FreeSubscriptionPlans = Extract<SubscriptionPlanId, SubscriptionPlanId.Free | SubscriptionPlanId.FreePlus>;
  10. export type PaidSubscriptionPlans = Exclude<SubscriptionPlanId, SubscriptionPlanId.Free | SubscriptionPlanId.FreePlus>;
  11. export type RequiredSubscriptionPlans = Exclude<SubscriptionPlanId, SubscriptionPlanId.Free>;
  12. export interface Subscription {
  13. readonly plan: {
  14. readonly actual: SubscriptionPlan;
  15. readonly effective: SubscriptionPlan;
  16. };
  17. account: SubscriptionAccount | undefined;
  18. previewTrial?: SubscriptionPreviewTrial;
  19. state: SubscriptionState;
  20. }
  21. export interface SubscriptionPlan {
  22. readonly id: SubscriptionPlanId;
  23. readonly name: string;
  24. readonly startedOn: string;
  25. readonly expiresOn?: string | undefined;
  26. }
  27. export interface SubscriptionAccount {
  28. readonly id: string;
  29. readonly name: string;
  30. readonly email: string | undefined;
  31. readonly verified: boolean;
  32. }
  33. export interface SubscriptionPreviewTrial {
  34. readonly startedOn: string;
  35. readonly expiresOn: string;
  36. }
  37. export const enum SubscriptionState {
  38. /** Indicates a user who hasn't verified their email address yet */
  39. VerificationRequired = -1,
  40. /** Indicates a Free user who hasn't yet started the preview trial */
  41. Free = 0,
  42. /** Indicates a Free user who is in preview trial */
  43. FreeInPreview,
  44. /** Indicates a Free user who's preview has expired trial */
  45. FreePreviewExpired,
  46. /** Indicates a Free+ user with a completed trial */
  47. FreePlusInTrial,
  48. /** Indicates a Free+ user who's trial has expired */
  49. FreePlusTrialExpired,
  50. /** Indicates a Paid user */
  51. Paid,
  52. }
  53. export function computeSubscriptionState(subscription: Optional<Subscription, 'state'>): SubscriptionState {
  54. const {
  55. account,
  56. plan: { actual, effective },
  57. previewTrial: preview,
  58. } = subscription;
  59. if (account?.verified === false) return SubscriptionState.VerificationRequired;
  60. if (actual.id === effective.id) {
  61. switch (effective.id) {
  62. case SubscriptionPlanId.Free:
  63. return preview == null ? SubscriptionState.Free : SubscriptionState.FreePreviewExpired;
  64. case SubscriptionPlanId.FreePlus:
  65. return SubscriptionState.FreePlusTrialExpired;
  66. case SubscriptionPlanId.Pro:
  67. case SubscriptionPlanId.Teams:
  68. case SubscriptionPlanId.Enterprise:
  69. return SubscriptionState.Paid;
  70. }
  71. }
  72. switch (effective.id) {
  73. case SubscriptionPlanId.Free:
  74. return preview == null ? SubscriptionState.Free : SubscriptionState.FreeInPreview;
  75. case SubscriptionPlanId.FreePlus:
  76. return SubscriptionState.FreePlusTrialExpired;
  77. case SubscriptionPlanId.Pro:
  78. return actual.id === SubscriptionPlanId.Free
  79. ? SubscriptionState.FreeInPreview
  80. : SubscriptionState.FreePlusInTrial;
  81. case SubscriptionPlanId.Teams:
  82. case SubscriptionPlanId.Enterprise:
  83. return SubscriptionState.Paid;
  84. }
  85. }
  86. export function getSubscriptionPlan(id: SubscriptionPlanId, startedOn?: Date, expiresOn?: Date): SubscriptionPlan {
  87. return {
  88. id: id,
  89. name: getSubscriptionPlanName(id),
  90. startedOn: (startedOn ?? new Date()).toISOString(),
  91. expiresOn: expiresOn != null ? expiresOn.toISOString() : undefined,
  92. };
  93. }
  94. export function getSubscriptionPlanName(id: SubscriptionPlanId) {
  95. switch (id) {
  96. case SubscriptionPlanId.FreePlus:
  97. return 'GitLens Free+';
  98. case SubscriptionPlanId.Pro:
  99. return 'GitLens Pro';
  100. case SubscriptionPlanId.Teams:
  101. return 'GitLens Teams';
  102. case SubscriptionPlanId.Enterprise:
  103. return 'GitLens Enterprise';
  104. case SubscriptionPlanId.Free:
  105. default:
  106. return 'GitLens Free';
  107. }
  108. }
  109. const plansPriority = new Map<SubscriptionPlanId | undefined, number>([
  110. [undefined, -1],
  111. [SubscriptionPlanId.Free, 0],
  112. [SubscriptionPlanId.FreePlus, 1],
  113. [SubscriptionPlanId.Pro, 2],
  114. [SubscriptionPlanId.Teams, 3],
  115. [SubscriptionPlanId.Enterprise, 4],
  116. ]);
  117. export function getSubscriptionPlanPriority(id: SubscriptionPlanId | undefined): number {
  118. return plansPriority.get(id)!;
  119. }
  120. export function getSubscriptionTimeRemaining(
  121. subscription: Optional<Subscription, 'state'>,
  122. unit?: 'days' | 'hours' | 'minutes' | 'seconds',
  123. ): number | undefined {
  124. return getTimeRemaining(subscription.plan.effective.expiresOn, unit);
  125. }
  126. export function getTimeRemaining(
  127. expiresOn: string | undefined,
  128. unit?: 'days' | 'hours' | 'minutes' | 'seconds',
  129. ): number | undefined {
  130. return expiresOn != null ? getDateDifference(Date.now(), new Date(expiresOn), unit) : undefined;
  131. }
  132. export function isSubscriptionPaid(subscription: Optional<Subscription, 'state'>): boolean {
  133. return isSubscriptionPaidPlan(subscription.plan.effective.id);
  134. }
  135. export function isSubscriptionPaidPlan(id: SubscriptionPlanId): id is PaidSubscriptionPlans {
  136. return id !== SubscriptionPlanId.Free && id !== SubscriptionPlanId.FreePlus;
  137. }
  138. export function isSubscriptionExpired(subscription: Optional<Subscription, 'state'>): boolean {
  139. const remaining = getSubscriptionTimeRemaining(subscription);
  140. return remaining != null && remaining <= 0;
  141. }
  142. export function isSubscriptionTrial(subscription: Optional<Subscription, 'state'>): boolean {
  143. return subscription.plan.actual.id !== subscription.plan.effective.id;
  144. }
  145. export function isSubscriptionPreviewTrialExpired(subscription: Optional<Subscription, 'state'>): boolean | undefined {
  146. const remaining = getTimeRemaining(subscription.previewTrial?.expiresOn);
  147. return remaining != null ? remaining <= 0 : undefined;
  148. }