diff --git a/apps/web/src/dialogs/settings/subscription-settings.ts b/apps/web/src/dialogs/settings/subscription-settings.ts index d50f8fac1..ad112a91e 100644 --- a/apps/web/src/dialogs/settings/subscription-settings.ts +++ b/apps/web/src/dialogs/settings/subscription-settings.ts @@ -163,25 +163,56 @@ export const SubscriptionSettings: SettingsGroup[] = [ title: "Cancel", variant: "error", async action() { - const cancelTrial = await ConfirmDialog.show({ - title: "Cancel trial?", - message: - "Cancel your trial to stop all future charges permanently. You will be immediately downgraded to the Free plan.", - negativeButtonText: "No", - positiveButtonText: "Yes" - }); - if (cancelTrial) { - await TaskManager.startTask({ - type: "modal", - title: "Cancelling your trial", - subtitle: "Please wait...", - action: () => db.subscriptions.cancel() - }) - .catch((e) => showToast("error", e.message)) - .then(() => - showToast("success", "Your trial has been canceled.") - ); + const canExtend = + !useUserStore.getState().user?.subscription?.extensionsAvailed + ?.length; + + if (canExtend) { + const isExtending = await ConfirmDialog.show({ + title: "We're sorry to see you go!", + message: + "Before you cancel, did you just need more time to test? We'd love to give you an extra 14 days, completely free.", + negativeButtonText: "No, cancel my trial", + positiveButtonText: "Yes, extend my trial" + }); + if (isExtending) { + const error = await TaskManager.startTask({ + type: "modal", + title: "Extending your trial", + subtitle: "Please wait...", + action: async () => { + await db.subscriptions.extend(); + await useUserStore.getState().refreshUser(); + showToast( + "success", + "Your trial has been extended by 14 days. Enjoy!" + ); + } + }); + if (error instanceof Error) showToast("error", error.message); + return; + } + } else { + const cancelTrial = await ConfirmDialog.show({ + title: "Cancel trial?", + message: + "Cancel your trial to stop all future charges permanently. You will be immediately downgraded to the Free plan.", + negativeButtonText: "No", + positiveButtonText: "Yes" + }); + if (!cancelTrial) return; } + + await TaskManager.startTask({ + type: "modal", + title: "Cancelling your trial", + subtitle: "Please wait...", + action: () => db.subscriptions.cancel() + }) + .catch((e) => showToast("error", e.message)) + .then(() => + showToast("success", "Your trial has been canceled.") + ); } } ] diff --git a/packages/core/src/api/subscriptions.ts b/packages/core/src/api/subscriptions.ts index fa88b44c8..dc390dbca 100644 --- a/packages/core/src/api/subscriptions.ts +++ b/packages/core/src/api/subscriptions.ts @@ -102,6 +102,14 @@ export default class Subscriptions { await http.post(`${hosts.SUBSCRIPTIONS_HOST}/${endpoint}`, null, token); } + async extend() { + const token = await this.db.tokenManager.getAccessToken(); + const user = await this.db.user.getUser(); + if (!token || !user || isLegacySubscription(user)) return; + const endpoint = `subscriptions/v2/extend`; + await http.post(`${hosts.SUBSCRIPTIONS_HOST}/${endpoint}`, null, token); + } + async pause() { const token = await this.db.tokenManager.getAccessToken(); const user = await this.db.user.getUser(); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 4cf8f2903..553badbf4 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -630,6 +630,12 @@ export enum SubscriptionProvider { GIFT_CARD = 4 } +type SubscriptionExtension = { + expiry: number; + timestamp: number; + type: "trial_extension"; +}; + export type User = { id: string; email: string; @@ -661,6 +667,7 @@ export type User = { plan: SubscriptionPlan; status: SubscriptionStatus; trialsAvailed?: SubscriptionPlan[]; + extensionsAvailed?: SubscriptionExtension[]; updateURL: string | null; googlePurchaseToken: string | null; }; diff --git a/packages/core/src/utils/constants.ts b/packages/core/src/utils/constants.ts index 5092da87b..1a41db5b2 100644 --- a/packages/core/src/utils/constants.ts +++ b/packages/core/src/utils/constants.ts @@ -52,7 +52,7 @@ export const hosts = { : "http://localhost:6264", NOTESNOOK_HOST: isProduction() ? "https://notesnook.com" - : "http://localhost:8787" + : "http://localhost:3001" }; export default hosts;