@umituz/react-native-subscription 2.22.7 → 2.22.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-subscription",
3
- "version": "2.22.7",
3
+ "version": "2.22.9",
4
4
  "description": "Complete subscription management with RevenueCat, paywall UI, and credits system for React Native apps",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -19,17 +19,34 @@ export type PurchaseSource =
19
19
 
20
20
  export type PurchaseType = "initial" | "renewal" | "upgrade" | "downgrade";
21
21
 
22
+ export type SubscriptionStatus = "active" | "expired" | "canceled" | "free";
23
+
24
+ /** Single Source of Truth for user subscription + credits data */
22
25
  export interface UserCredits {
26
+ // Core subscription
27
+ isPremium: boolean;
28
+ status: SubscriptionStatus;
29
+
30
+ // Dates
31
+ purchasedAt: Date | null;
32
+ expirationDate: Date | null;
33
+ lastUpdatedAt: Date | null;
34
+
35
+ // RevenueCat subscription details
36
+ willRenew: boolean;
37
+ productId?: string;
38
+ packageType?: "weekly" | "monthly" | "yearly" | "lifetime";
39
+ originalTransactionId?: string;
40
+
41
+ // Credits
23
42
  credits: number;
24
- packageType?: "weekly" | "monthly" | "yearly";
25
43
  creditLimit?: number;
26
- productId?: string;
44
+
45
+ // Metadata
27
46
  purchaseSource?: PurchaseSource;
28
47
  purchaseType?: PurchaseType;
29
48
  platform?: "ios" | "android";
30
49
  appVersion?: string;
31
- purchasedAt: Date | null;
32
- lastUpdatedAt: Date | null;
33
50
  }
34
51
 
35
52
  export interface CreditAllocation {
@@ -1,27 +1,48 @@
1
- import type { UserCredits } from "../../domain/entities/Credits";
1
+ import type { UserCredits, SubscriptionStatus } from "../../domain/entities/Credits";
2
2
  import type { UserCreditsDocumentRead } from "../models/UserCreditsDocument";
3
3
 
4
+ /** Maps Firestore document to domain entity */
4
5
  export class CreditsMapper {
5
- static toEntity(snapData: UserCreditsDocumentRead): UserCredits {
6
+ static toEntity(doc: UserCreditsDocumentRead): UserCredits {
7
+ // Determine status from document or derive from isPremium/expirationDate
8
+ const status = doc.status ?? CreditsMapper.deriveStatus(doc);
9
+
6
10
  return {
7
- credits: snapData.credits,
8
- packageType: snapData.packageType,
9
- creditLimit: snapData.creditLimit,
10
- productId: snapData.productId,
11
- purchaseSource: snapData.purchaseSource,
12
- purchaseType: snapData.purchaseType,
13
- platform: snapData.platform,
14
- appVersion: snapData.appVersion,
15
- purchasedAt: snapData.purchasedAt?.toDate?.() || null,
16
- lastUpdatedAt: snapData.lastUpdatedAt?.toDate?.() || null,
11
+ // Core subscription
12
+ isPremium: doc.isPremium ?? false,
13
+ status,
14
+
15
+ // Dates
16
+ purchasedAt: doc.purchasedAt?.toDate?.() ?? null,
17
+ expirationDate: doc.expirationDate?.toDate?.() ?? null,
18
+ lastUpdatedAt: doc.lastUpdatedAt?.toDate?.() ?? null,
19
+
20
+ // RevenueCat details
21
+ willRenew: doc.willRenew ?? false,
22
+ productId: doc.productId,
23
+ packageType: doc.packageType,
24
+ originalTransactionId: doc.originalTransactionId,
25
+
26
+ // Credits
27
+ credits: doc.credits,
28
+ creditLimit: doc.creditLimit,
29
+
30
+ // Metadata
31
+ purchaseSource: doc.purchaseSource,
32
+ purchaseType: doc.purchaseType,
33
+ platform: doc.platform,
34
+ appVersion: doc.appVersion,
17
35
  };
18
36
  }
19
37
 
20
- static toFirestore(data: Partial<UserCredits>): Record<string, any> {
21
- return {
22
- credits: data.credits,
23
- // Timestamps are usually handled by serverTimestamp() in repos,
24
- // but we can map them if needed.
25
- };
38
+ /** Derive status from isPremium and expirationDate for backward compatibility */
39
+ private static deriveStatus(doc: UserCreditsDocumentRead): SubscriptionStatus {
40
+ if (!doc.isPremium && !doc.expirationDate) return "free";
41
+ if (doc.isPremium) return "active";
42
+ if (doc.expirationDate) {
43
+ const expDate = doc.expirationDate.toDate?.();
44
+ if (expDate && expDate < new Date()) return "expired";
45
+ }
46
+ return "free";
26
47
  }
27
48
  }
@@ -1,4 +1,3 @@
1
-
2
1
  export interface FirestoreTimestamp {
3
2
  toDate: () => Date;
4
3
  }
@@ -13,9 +12,11 @@ export type PurchaseSource =
13
12
 
14
13
  export type PurchaseType = "initial" | "renewal" | "upgrade" | "downgrade";
15
14
 
15
+ export type SubscriptionDocStatus = "active" | "expired" | "canceled" | "free";
16
+
16
17
  export interface PurchaseMetadata {
17
18
  productId: string;
18
- packageType: "weekly" | "monthly" | "yearly";
19
+ packageType: "weekly" | "monthly" | "yearly" | "lifetime";
19
20
  creditLimit: number;
20
21
  source: PurchaseSource;
21
22
  type: PurchaseType;
@@ -24,19 +25,33 @@ export interface PurchaseMetadata {
24
25
  timestamp: FirestoreTimestamp;
25
26
  }
26
27
 
27
- // Document structure when READING from Firestore
28
+ /** Single Source of Truth for user subscription data */
28
29
  export interface UserCreditsDocumentRead {
30
+ // Core subscription status
31
+ isPremium?: boolean;
32
+ status?: SubscriptionDocStatus;
33
+
34
+ // Dates (all from RevenueCat)
35
+ purchasedAt?: FirestoreTimestamp;
36
+ expirationDate?: FirestoreTimestamp;
37
+ lastUpdatedAt?: FirestoreTimestamp;
38
+ lastPurchaseAt?: FirestoreTimestamp;
39
+
40
+ // RevenueCat subscription details
41
+ willRenew?: boolean;
42
+ productId?: string;
43
+ packageType?: "weekly" | "monthly" | "yearly" | "lifetime";
44
+ originalTransactionId?: string;
45
+
46
+ // Credits
29
47
  credits: number;
30
- packageType?: "weekly" | "monthly" | "yearly";
31
48
  creditLimit?: number;
32
- productId?: string;
49
+
50
+ // Metadata
33
51
  purchaseSource?: PurchaseSource;
34
52
  purchaseType?: PurchaseType;
35
53
  platform?: "ios" | "android";
36
54
  appVersion?: string;
37
- purchasedAt?: FirestoreTimestamp;
38
- lastUpdatedAt?: FirestoreTimestamp;
39
- lastPurchaseAt?: FirestoreTimestamp;
40
55
  processedPurchases?: string[];
41
56
  purchaseHistory?: PurchaseMetadata[];
42
57
  }
@@ -14,12 +14,20 @@ import { getCreditAllocation } from "../../utils/creditMapper";
14
14
 
15
15
  import { CreditsMapper } from "../mappers/CreditsMapper";
16
16
 
17
+ /** RevenueCat subscription data to save (Single Source of Truth) */
18
+ export interface RevenueCatData {
19
+ expirationDate?: string | null;
20
+ willRenew?: boolean;
21
+ originalTransactionId?: string;
22
+ isPremium?: boolean;
23
+ }
24
+
17
25
  export class CreditsRepository extends BaseRepository {
18
26
  constructor(private config: CreditsConfig) { super(); }
19
27
 
20
28
  private getRef(db: Firestore, userId: string) {
21
- return this.config.useUserSubcollection
22
- ? doc(db, "users", userId, "credits", "balance")
29
+ return this.config.useUserSubcollection
30
+ ? doc(db, "users", userId, "credits", "balance")
23
31
  : doc(db, this.config.collectionName, userId);
24
32
  }
25
33
 
@@ -51,7 +59,8 @@ export class CreditsRepository extends BaseRepository {
51
59
  userId: string,
52
60
  purchaseId?: string,
53
61
  productId?: string,
54
- source?: PurchaseSource
62
+ source?: PurchaseSource,
63
+ revenueCatData?: RevenueCatData
55
64
  ): Promise<CreditsResult> {
56
65
  const db = getFirestore();
57
66
  if (!db) return { success: false, error: { message: "No DB", code: "INIT_ERR" } };
@@ -70,6 +79,11 @@ export class CreditsRepository extends BaseRepository {
70
79
  const metadata: InitializeCreditsMetadata = {
71
80
  productId,
72
81
  source,
82
+ // RevenueCat data for Single Source of Truth
83
+ expirationDate: revenueCatData?.expirationDate,
84
+ willRenew: revenueCatData?.willRenew,
85
+ originalTransactionId: revenueCatData?.originalTransactionId,
86
+ isPremium: revenueCatData?.isPremium,
73
87
  };
74
88
 
75
89
  const res = await initializeCreditsTransaction(
@@ -3,6 +3,7 @@ import Constants from "expo-constants";
3
3
  import {
4
4
  runTransaction,
5
5
  serverTimestamp,
6
+ Timestamp,
6
7
  type Firestore,
7
8
  type FieldValue,
8
9
  type Transaction,
@@ -14,6 +15,7 @@ import type {
14
15
  PurchaseSource,
15
16
  PurchaseType,
16
17
  PurchaseMetadata,
18
+ SubscriptionDocStatus,
17
19
  } from "../models/UserCreditsDocument";
18
20
  import { detectPackageType } from "../../utils/packageTypeDetector";
19
21
  import { getCreditAllocation } from "../../utils/creditMapper";
@@ -22,10 +24,16 @@ interface InitializationResult {
22
24
  credits: number;
23
25
  }
24
26
 
27
+ /** RevenueCat data to save to Firestore (Single Source of Truth) */
25
28
  export interface InitializeCreditsMetadata {
26
29
  productId?: string;
27
30
  source?: PurchaseSource;
28
31
  type?: PurchaseType;
32
+ // RevenueCat subscription data
33
+ expirationDate?: string | null;
34
+ willRenew?: boolean;
35
+ originalTransactionId?: string;
36
+ isPremium?: boolean;
29
37
  }
30
38
 
31
39
  export async function initializeCreditsTransaction(
@@ -114,17 +122,41 @@ export async function initializeCreditsTransaction(
114
122
  ? [...(existing?.purchaseHistory || []), purchaseMetadata].slice(-10)
115
123
  : existing?.purchaseHistory;
116
124
 
117
- // Build credits data, excluding undefined values (Firestore doesn't accept undefined)
125
+ // Determine subscription status
126
+ const isPremium = metadata?.isPremium ?? true;
127
+ const status: SubscriptionDocStatus = isPremium ? "active" : "expired";
128
+
129
+ // Build credits data (Single Source of Truth)
118
130
  const creditsData: Record<string, unknown> = {
131
+ // Core subscription
132
+ isPremium,
133
+ status,
134
+
135
+ // Credits
119
136
  credits: newCredits,
120
137
  creditLimit,
138
+
139
+ // Dates
121
140
  purchasedAt,
122
141
  lastUpdatedAt: now,
123
142
  lastPurchaseAt: now,
143
+
144
+ // Tracking
124
145
  processedPurchases,
125
146
  };
126
147
 
127
- // Only add optional fields if they have values
148
+ // RevenueCat subscription data
149
+ if (metadata?.expirationDate) {
150
+ creditsData.expirationDate = Timestamp.fromDate(new Date(metadata.expirationDate));
151
+ }
152
+ if (metadata?.willRenew !== undefined) {
153
+ creditsData.willRenew = metadata.willRenew;
154
+ }
155
+ if (metadata?.originalTransactionId) {
156
+ creditsData.originalTransactionId = metadata.originalTransactionId;
157
+ }
158
+
159
+ // Package info
128
160
  if (packageType && packageType !== "unknown") {
129
161
  creditsData.packageType = packageType;
130
162
  }
@@ -133,6 +165,8 @@ export async function initializeCreditsTransaction(
133
165
  creditsData.platform = platform;
134
166
  creditsData.appVersion = appVersion;
135
167
  }
168
+
169
+ // Purchase metadata
136
170
  if (metadata?.source) {
137
171
  creditsData.purchaseSource = metadata.source;
138
172
  }
@@ -3,10 +3,12 @@
3
3
  */
4
4
 
5
5
  import { Platform } from "react-native";
6
+ import type { CustomerInfo } from "react-native-purchases";
6
7
  import type { CreditsConfig } from "../../domain/entities/Credits";
7
8
  import { configureCreditsRepository, getCreditsRepository } from "../repositories/CreditsRepositoryProvider";
8
9
  import { SubscriptionManager } from "../../revenuecat/infrastructure/managers/SubscriptionManager";
9
10
  import { configureAuthProvider } from "../../presentation/hooks/useAuthAwarePurchase";
11
+ import type { RevenueCatData } from "../repositories/CreditsRepository";
10
12
 
11
13
  export interface FirebaseAuthLike {
12
14
  currentUser: { uid: string; isAnonymous: boolean } | null;
@@ -39,16 +41,31 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
39
41
 
40
42
  configureCreditsRepository({ ...credits, creditPackageAmounts: creditPackages?.amounts });
41
43
 
42
- const onPurchase = async (userId: string, productId: string, _customerInfo: unknown, source?: string) => {
44
+ /** Extract RevenueCat data from CustomerInfo (Single Source of Truth) */
45
+ const extractRevenueCatData = (customerInfo: CustomerInfo, _productId: string): RevenueCatData => {
46
+ const entitlement = customerInfo.entitlements.active[entitlementId]
47
+ ?? customerInfo.entitlements.all[entitlementId];
48
+
49
+ return {
50
+ expirationDate: entitlement?.expirationDate ?? customerInfo.latestExpirationDate ?? null,
51
+ willRenew: entitlement?.willRenew ?? false,
52
+ originalTransactionId: entitlement?.originalPurchaseDate ?? undefined,
53
+ isPremium: Object.keys(customerInfo.entitlements.active).length > 0,
54
+ };
55
+ };
56
+
57
+ const onPurchase = async (userId: string, productId: string, customerInfo: CustomerInfo, source?: string) => {
43
58
  if (__DEV__) {
44
59
  console.log('[SubscriptionInitializer] onPurchase called:', { userId, productId, source });
45
60
  }
46
61
  try {
62
+ const revenueCatData = extractRevenueCatData(customerInfo, productId);
47
63
  const result = await getCreditsRepository().initializeCredits(
48
64
  userId,
49
65
  `purchase_${productId}_${Date.now()}`,
50
66
  productId,
51
- source as any
67
+ source as any,
68
+ revenueCatData
52
69
  );
53
70
  if (__DEV__) {
54
71
  console.log('[SubscriptionInitializer] Credits initialized:', result);
@@ -61,16 +78,20 @@ export const initializeSubscription = async (config: SubscriptionInitConfig): Pr
61
78
  }
62
79
  };
63
80
 
64
- const onRenewal = async (userId: string, productId: string, _newExpirationDate: string, _customerInfo: unknown) => {
81
+ const onRenewal = async (userId: string, productId: string, newExpirationDate: string, customerInfo: CustomerInfo) => {
65
82
  if (__DEV__) {
66
83
  console.log('[SubscriptionInitializer] onRenewal called:', { userId, productId });
67
84
  }
68
85
  try {
86
+ const revenueCatData = extractRevenueCatData(customerInfo, productId);
87
+ // Update expiration date from renewal
88
+ revenueCatData.expirationDate = newExpirationDate || revenueCatData.expirationDate;
69
89
  const result = await getCreditsRepository().initializeCredits(
70
90
  userId,
71
91
  `renewal_${productId}_${Date.now()}`,
72
92
  productId,
73
- "renewal" as any
93
+ "renewal" as any,
94
+ revenueCatData
74
95
  );
75
96
  if (__DEV__) {
76
97
  console.log('[SubscriptionInitializer] Credits reset on renewal:', result);
@@ -1,21 +1,16 @@
1
1
  /**
2
2
  * useSubscriptionSettingsConfig Hook
3
3
  * Returns ready-to-use config for settings screens
4
- * Package-driven: all logic handled internally
4
+ * Single Source of Truth: Firestore (credits document)
5
5
  */
6
6
 
7
7
  import { useMemo, useCallback } from "react";
8
8
  import { useCredits } from "./useCredits";
9
- import { useSubscriptionStatus } from "./useSubscriptionStatus";
10
- import { useCustomerInfo } from "../../revenuecat/presentation/hooks/useCustomerInfo";
11
9
  import { usePaywallVisibility } from "./usePaywallVisibility";
12
10
  import { calculateDaysRemaining } from "../../domain/entities/SubscriptionStatus";
13
- import { SubscriptionManager } from "../../revenuecat/infrastructure/managers/SubscriptionManager";
14
- import { formatDate, convertPurchasedAt } from "../utils/subscriptionDateUtils";
11
+ import { formatDate } from "../utils/subscriptionDateUtils";
15
12
  import { useCreditsArray, getSubscriptionStatusType } from "./useSubscriptionSettingsConfig.utils";
16
13
  import { getCreditsConfig } from "../../infrastructure/repositories/CreditsRepositoryProvider";
17
- import { detectPackageType } from "../../utils/packageTypeDetector";
18
- import { getCreditAllocation } from "../../utils/creditMapper";
19
14
  import type {
20
15
  SubscriptionSettingsConfig,
21
16
  SubscriptionStatusType,
@@ -31,173 +26,103 @@ export type {
31
26
 
32
27
  /**
33
28
  * Hook that returns ready-to-use subscription config for settings
34
- * All business logic handled internally
29
+ * Single Source of Truth: Firestore credits document
35
30
  */
36
31
  export const useSubscriptionSettingsConfig = (
37
32
  params: UseSubscriptionSettingsConfigParams
38
33
  ): SubscriptionSettingsConfig => {
39
- const {
40
- userId,
41
- translations,
42
- creditLimit,
43
- upgradePrompt,
44
- } = params;
34
+ const { userId, translations, creditLimit, upgradePrompt } = params;
45
35
 
46
- // Internal hooks
36
+ // Single Source of Truth: Firestore credits document
47
37
  const { credits } = useCredits({ userId, enabled: !!userId });
48
- const {
49
- isPremium: subscriptionActive,
50
- expirationDate: statusExpirationDate,
51
- } = useSubscriptionStatus({
52
- userId,
53
- enabled: !!userId,
54
- });
55
- const { customerInfo } = useCustomerInfo();
56
38
  const { openPaywall } = usePaywallVisibility();
57
39
 
58
40
  const handleOpenPaywall = useCallback(() => {
59
41
  openPaywall("settings");
60
42
  }, [openPaywall]);
61
43
 
62
- // RevenueCat entitlement info - dynamically using configured entitlementId
63
- const entitlementId = SubscriptionManager.getEntitlementId() || "premium";
64
- const premiumEntitlement = customerInfo?.entitlements.active[entitlementId];
44
+ // All data from Firestore (Single Source of Truth)
45
+ const isPremium = credits?.isPremium ?? false;
46
+ const willRenew = credits?.willRenew ?? false;
65
47
 
66
- // Premium status: use customerInfo directly as it updates in real-time via listener
67
- // This is the source of truth, subscriptionActive is just a backup
68
- const isPremium = !!premiumEntitlement || subscriptionActive;
48
+ // Expiration date from Firestore
49
+ const expiresAtIso = credits?.expirationDate?.toISOString() ?? null;
69
50
 
51
+ // Purchase date from Firestore
52
+ const purchasedAtIso = credits?.purchasedAt?.toISOString() ?? null;
53
+
54
+ // Credit limit from Firestore or config fallback
70
55
  const dynamicCreditLimit = useMemo(() => {
56
+ if (credits?.creditLimit) return credits.creditLimit;
71
57
  const config = getCreditsConfig();
72
-
73
- // 1. ÖNCE FIRESTORE'DAN OKU (Single Source of Truth)
74
- if (credits?.creditLimit) {
75
- return credits.creditLimit;
76
- }
77
-
78
- // 2. FALLBACK: RevenueCat'ten detect et
79
- if (premiumEntitlement?.productIdentifier) {
80
- const packageType = detectPackageType(premiumEntitlement.productIdentifier);
81
- const allocation = getCreditAllocation(packageType, config.packageAllocations);
82
- if (allocation !== null) return allocation;
83
- }
84
-
85
- // 3. LAST RESORT: Credit miktarına bakarak tahmin et
86
- if (credits?.credits && config.packageAllocations) {
87
- const currentCredits = credits.credits;
88
- const allocations = Object.values(config.packageAllocations).map(a => a.credits);
89
- const closest = allocations.find(a => a >= currentCredits) || Math.max(...allocations);
90
- return closest;
91
- }
92
-
93
- // 4. FINAL FALLBACK: Config'den al
94
58
  return creditLimit ?? config.creditLimit;
95
- }, [credits?.creditLimit, credits?.credits, premiumEntitlement?.productIdentifier, creditLimit]);
96
-
97
- // Get expiration date directly from RevenueCat (source of truth)
98
- const entitlementExpirationDate = premiumEntitlement?.expirationDate ?? null;
99
-
100
- // Prefer CustomerInfo expiration (real-time) over cached status
101
- const expiresAtIso = entitlementExpirationDate || (statusExpirationDate
102
- ? statusExpirationDate.toISOString()
103
- : null);
104
-
105
-
106
-
107
- const willRenew = premiumEntitlement?.willRenew || false;
108
- const purchasedAtIso = convertPurchasedAt(credits?.purchasedAt);
59
+ }, [credits?.creditLimit, creditLimit]);
109
60
 
110
61
  // Formatted dates
111
- const formattedExpirationDate = useMemo(
112
- () => formatDate(expiresAtIso),
113
- [expiresAtIso]
114
- );
62
+ const formattedExpirationDate = useMemo(() => formatDate(expiresAtIso), [expiresAtIso]);
63
+ const formattedPurchaseDate = useMemo(() => formatDate(purchasedAtIso), [purchasedAtIso]);
115
64
 
116
- const formattedPurchaseDate = useMemo(
117
- () => formatDate(purchasedAtIso),
118
- [purchasedAtIso]
119
- );
65
+ // Days remaining
66
+ const daysRemaining = useMemo(() => calculateDaysRemaining(expiresAtIso), [expiresAtIso]);
120
67
 
121
- // Days remaining calculation
122
- const daysRemaining = useMemo(
123
- () => calculateDaysRemaining(expiresAtIso),
124
- [expiresAtIso]
125
- );
126
-
127
- // Status type
128
- const statusType: SubscriptionStatusType = getSubscriptionStatusType(isPremium);
68
+ // Status type from Firestore or derived
69
+ const statusType: SubscriptionStatusType = credits?.status
70
+ ? (credits.status as SubscriptionStatusType)
71
+ : getSubscriptionStatusType(isPremium);
129
72
 
130
73
  const creditsArray = useCreditsArray(credits, dynamicCreditLimit, translations);
131
74
 
132
- // Centralized display flags - single source of truth for UI visibility
75
+ // Centralized display flags
133
76
  const hasCredits = creditsArray.length > 0;
134
77
  const display = useMemo(() => ({
135
78
  showHeader: isPremium || hasCredits,
136
79
  showCredits: hasCredits,
137
80
  showUpgradePrompt: !isPremium && !hasCredits && !!upgradePrompt,
138
- showExpirationDate: isPremium && !!expiresAtIso,
81
+ showExpirationDate: (isPremium || hasCredits) && !!expiresAtIso,
139
82
  }), [isPremium, hasCredits, upgradePrompt, expiresAtIso]);
140
83
 
141
84
  // Build config
142
- const config = useMemo(
143
- (): SubscriptionSettingsConfig => ({
144
- enabled: true,
145
- settingsItem: {
146
- title: translations.title,
147
- description: translations.description,
148
- isPremium,
149
- statusLabel: isPremium
150
- ? translations.statusActive
151
- : translations.statusFree,
152
- icon: "diamond",
153
- onPress: handleOpenPaywall,
154
- },
155
- sectionConfig: {
156
- statusType,
157
- isPremium,
158
- display,
159
- expirationDate: formattedExpirationDate,
160
- purchaseDate: formattedPurchaseDate,
161
- isLifetime: isPremium && !expiresAtIso,
162
- daysRemaining,
163
- willRenew,
164
- credits: creditsArray,
165
- translations: {
166
- title: translations.title,
167
- statusLabel: translations.statusLabel,
168
- statusActive: translations.statusActive,
169
- statusExpired: translations.statusExpired,
170
- statusFree: translations.statusFree,
171
- statusCanceled: translations.statusCanceled,
172
- expiresLabel: translations.expiresLabel,
173
- purchasedLabel: translations.purchasedLabel,
174
- lifetimeLabel: translations.lifetimeLabel,
175
- creditsTitle: translations.creditsTitle,
176
- remainingLabel: translations.remainingLabel,
177
- manageButton: translations.manageButton,
178
- upgradeButton: translations.upgradeButton,
179
- },
180
- onUpgrade: handleOpenPaywall,
181
- upgradePrompt,
182
- },
183
- }),
184
- [
185
- translations,
85
+ return useMemo((): SubscriptionSettingsConfig => ({
86
+ enabled: true,
87
+ settingsItem: {
88
+ title: translations.title,
89
+ description: translations.description,
186
90
  isPremium,
91
+ statusLabel: isPremium ? translations.statusActive : translations.statusFree,
92
+ icon: "diamond",
93
+ onPress: handleOpenPaywall,
94
+ },
95
+ sectionConfig: {
187
96
  statusType,
97
+ isPremium,
188
98
  display,
189
- formattedExpirationDate,
190
- formattedPurchaseDate,
191
- expiresAtIso,
99
+ expirationDate: formattedExpirationDate,
100
+ purchaseDate: formattedPurchaseDate,
101
+ isLifetime: isPremium && !expiresAtIso,
192
102
  daysRemaining,
193
103
  willRenew,
194
- creditsArray,
195
- handleOpenPaywall,
104
+ credits: creditsArray,
105
+ translations: {
106
+ title: translations.title,
107
+ statusLabel: translations.statusLabel,
108
+ statusActive: translations.statusActive,
109
+ statusExpired: translations.statusExpired,
110
+ statusFree: translations.statusFree,
111
+ statusCanceled: translations.statusCanceled,
112
+ expiresLabel: translations.expiresLabel,
113
+ purchasedLabel: translations.purchasedLabel,
114
+ lifetimeLabel: translations.lifetimeLabel,
115
+ creditsTitle: translations.creditsTitle,
116
+ remainingLabel: translations.remainingLabel,
117
+ manageButton: translations.manageButton,
118
+ upgradeButton: translations.upgradeButton,
119
+ },
120
+ onUpgrade: handleOpenPaywall,
196
121
  upgradePrompt,
197
- ]
198
- );
199
-
200
-
201
-
202
- return config;
122
+ },
123
+ }), [
124
+ translations, isPremium, statusType, display, formattedExpirationDate,
125
+ formattedPurchaseDate, expiresAtIso, daysRemaining, willRenew,
126
+ creditsArray, handleOpenPaywall, upgradePrompt,
127
+ ]);
203
128
  };