@umituz/react-native-subscription 2.27.91 → 2.27.93
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 +1 -1
- package/src/domains/credits/application/CreditsInitializer.ts +91 -38
- package/src/domains/credits/core/UserCreditsDocument.ts +33 -33
- package/src/domains/credits/infrastructure/CreditsRepository.ts +44 -58
- package/src/domains/paywall/components/PaywallModal.tsx +1 -1
- package/src/domains/subscription/application/SubscriptionInitializer.ts +59 -18
- package/src/domains/subscription/core/RevenueCatTypes.ts +2 -1
- package/src/domains/subscription/core/SubscriptionConstants.ts +12 -0
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +46 -27
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +106 -42
- package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +4 -2
- package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +1 -2
- package/src/domains/subscription/infrastructure/utils/RenewalDetector.ts +1 -1
- package/src/{presentation → domains/subscription/presentation}/components/details/PremiumStatusBadge.tsx +6 -4
- package/src/{presentation → domains/subscription/presentation}/components/feedback/PaywallFeedbackModal.tsx +1 -1
- package/src/{presentation → domains/subscription/presentation}/types/SubscriptionDetailTypes.ts +4 -2
- package/src/{presentation → domains/subscription/presentation}/types/SubscriptionSettingsTypes.ts +1 -1
- package/src/domains/subscription/presentation/usePremiumGate.ts +1 -1
- package/src/domains/subscription/presentation/useSavedPurchaseAutoExecution.ts +1 -1
- package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.ts +4 -3
- package/src/domains/subscription/presentation/useSubscriptionSettingsConfig.utils.ts +1 -1
- package/src/domains/trial/application/TrialEligibilityService.ts +1 -1
- package/src/domains/trial/infrastructure/DeviceTrialRepository.ts +2 -2
- package/src/index.ts +5 -5
- package/src/shared/application/ports/IRevenueCatService.ts +2 -0
- package/src/shared/infrastructure/SubscriptionEventBus.ts +5 -2
- package/src/utils/tierUtils.ts +3 -3
- package/src/utils/types.ts +4 -1
- package/src/utils/validation.ts +2 -2
- package/src/domains/config/README.md +0 -100
- package/src/presentation/README.md +0 -125
- package/src/presentation/hooks/README.md +0 -156
- package/src/presentation/hooks/useAuthSubscriptionSync.md +0 -94
- package/src/presentation/hooks/useCredits.md +0 -103
- package/src/presentation/hooks/useDeductCredit.md +0 -100
- package/src/presentation/hooks/useFeatureGate.md +0 -112
- package/src/presentation/hooks/usePaywall.md +0 -89
- package/src/presentation/hooks/usePaywallOperations.md +0 -92
- package/src/presentation/hooks/usePaywallVisibility.md +0 -95
- package/src/presentation/hooks/usePremium.md +0 -88
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +0 -94
- package/src/utils/README.md +0 -42
- /package/src/{presentation → domains/subscription/presentation}/components/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/CreditRow.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/CreditRow.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/DetailRow.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/DetailRow.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.styles.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCard.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumDetailsCardTypes.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/PremiumStatusBadge.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/details/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/feedback/PaywallFeedbackModal.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/feedback/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/feedback/paywallFeedbackStyles.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/overlay/PurchaseLoadingOverlay.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/overlay/index.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/paywall/PaywallModal.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/paywall/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/sections/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/sections/SubscriptionSection.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/components/sections/SubscriptionSection.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/SubscriptionDetailScreen.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/components/CreditsList.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/components/DevTestSection.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/components/SubscriptionActions.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/components/SubscriptionHeader.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/screens/components/UpgradePrompt.tsx +0 -0
- /package/src/{presentation → domains/subscription/presentation}/stores/index.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/stores/purchaseLoadingStore.ts +0 -0
- /package/src/{presentation → domains/subscription/presentation}/types/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/utils/README.md +0 -0
- /package/src/{presentation → domains/subscription/presentation}/utils/subscriptionDateUtils.ts +0 -0
|
@@ -14,7 +14,7 @@ import { SubscriptionInternalState } from "./SubscriptionInternalState";
|
|
|
14
14
|
export interface SubscriptionManagerConfig {
|
|
15
15
|
config: RevenueCatConfig;
|
|
16
16
|
apiKey: string;
|
|
17
|
-
getAnonymousUserId
|
|
17
|
+
getAnonymousUserId: () => Promise<string>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
class SubscriptionManagerImpl {
|
|
@@ -25,97 +25,161 @@ class SubscriptionManagerImpl {
|
|
|
25
25
|
|
|
26
26
|
configure(config: SubscriptionManagerConfig): void {
|
|
27
27
|
this.managerConfig = config;
|
|
28
|
-
this.
|
|
29
|
-
|
|
28
|
+
this.state.userIdProvider.configure(config.getAnonymousUserId);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private ensurePackageHandlerInitialized(): void {
|
|
32
|
+
if (this.packageHandler) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!this.serviceInstance) {
|
|
37
|
+
throw new Error("Service instance not available");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!this.managerConfig) {
|
|
41
|
+
throw new Error("Manager not configured");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.packageHandler = new PackageHandler(
|
|
45
|
+
this.serviceInstance,
|
|
46
|
+
this.managerConfig.config.entitlementIdentifier
|
|
47
|
+
);
|
|
30
48
|
}
|
|
31
49
|
|
|
32
50
|
private ensureConfigured(): void {
|
|
33
|
-
if (!this.managerConfig
|
|
51
|
+
if (!this.managerConfig) {
|
|
52
|
+
throw new Error("SubscriptionManager not configured");
|
|
53
|
+
}
|
|
34
54
|
}
|
|
35
55
|
|
|
36
|
-
async initialize(userId
|
|
56
|
+
async initialize(userId: string): Promise<boolean> {
|
|
37
57
|
this.ensureConfigured();
|
|
38
|
-
const effectiveUserId = userId || (await this.state.userIdProvider.getOrCreateAnonymousUserId());
|
|
39
|
-
const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(effectiveUserId);
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
const { shouldInit, existingPromise } = this.state.initCache.tryAcquireInitialization(userId);
|
|
60
|
+
|
|
61
|
+
if (!shouldInit && existingPromise) {
|
|
62
|
+
return existingPromise;
|
|
63
|
+
}
|
|
42
64
|
|
|
43
65
|
const promise = (async () => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
console.error('[SubscriptionManager] Service instance not available after initialization');
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
this.packageHandler!.setService(this.serviceInstance);
|
|
54
|
-
const result = await this.serviceInstance.initialize(effectiveUserId);
|
|
55
|
-
return result.success;
|
|
56
|
-
} catch (error) {
|
|
57
|
-
if (__DEV__) {
|
|
58
|
-
console.error('[SubscriptionManager] Initialization failed:', error);
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
66
|
+
await initializeRevenueCatService(this.managerConfig!.config);
|
|
67
|
+
this.serviceInstance = getRevenueCatService();
|
|
68
|
+
|
|
69
|
+
if (!this.serviceInstance) {
|
|
70
|
+
throw new Error("Service instance not available after initialization");
|
|
61
71
|
}
|
|
72
|
+
|
|
73
|
+
this.ensurePackageHandlerInitialized();
|
|
74
|
+
const result = await this.serviceInstance.initialize(userId);
|
|
75
|
+
return result.success;
|
|
62
76
|
})();
|
|
63
77
|
|
|
64
|
-
this.state.initCache.setPromise(promise,
|
|
78
|
+
this.state.initCache.setPromise(promise, userId);
|
|
65
79
|
return promise;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
isInitializedForUser(userId: string): boolean {
|
|
69
|
-
|
|
83
|
+
if (!this.serviceInstance) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!this.serviceInstance.isInitialized()) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.state.initCache.getCurrentUserId() === userId;
|
|
70
92
|
}
|
|
71
93
|
|
|
72
94
|
async getPackages(): Promise<PurchasesPackage[]> {
|
|
73
95
|
this.ensureConfigured();
|
|
96
|
+
|
|
74
97
|
if (!this.serviceInstance) {
|
|
75
98
|
this.serviceInstance = getRevenueCatService();
|
|
76
|
-
this.packageHandler!.setService(this.serviceInstance);
|
|
77
99
|
}
|
|
100
|
+
|
|
101
|
+
if (!this.serviceInstance) {
|
|
102
|
+
throw new Error("Service instance not available");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.ensurePackageHandlerInitialized();
|
|
78
106
|
return this.packageHandler!.fetchPackages();
|
|
79
107
|
}
|
|
80
108
|
|
|
81
109
|
async purchasePackage(pkg: PurchasesPackage): Promise<boolean> {
|
|
82
110
|
this.ensureConfigured();
|
|
111
|
+
|
|
83
112
|
const userId = this.state.initCache.getCurrentUserId();
|
|
84
|
-
if (!userId)
|
|
113
|
+
if (!userId) {
|
|
114
|
+
throw new Error("No current user found");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.ensurePackageHandlerInitialized();
|
|
85
118
|
return this.packageHandler!.purchase(pkg, userId);
|
|
86
119
|
}
|
|
87
120
|
|
|
88
121
|
async restore(): Promise<RestoreResultInfo> {
|
|
89
122
|
this.ensureConfigured();
|
|
123
|
+
|
|
90
124
|
const userId = this.state.initCache.getCurrentUserId();
|
|
91
|
-
if (!userId)
|
|
125
|
+
if (!userId) {
|
|
126
|
+
throw new Error("No current user found");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.ensurePackageHandlerInitialized();
|
|
92
130
|
return this.packageHandler!.restore(userId);
|
|
93
131
|
}
|
|
94
132
|
|
|
95
133
|
async checkPremiumStatus(): Promise<PremiumStatus> {
|
|
96
134
|
this.ensureConfigured();
|
|
135
|
+
|
|
97
136
|
const userId = this.state.initCache.getCurrentUserId();
|
|
98
|
-
if (!userId)
|
|
137
|
+
if (!userId) {
|
|
138
|
+
throw new Error("No current user found");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!this.serviceInstance) {
|
|
142
|
+
throw new Error("Service instance not available");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const customerInfo = await this.serviceInstance.getCustomerInfo();
|
|
99
146
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (customerInfo) return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
throw error;
|
|
147
|
+
if (!customerInfo) {
|
|
148
|
+
throw new Error("Customer info not available");
|
|
105
149
|
}
|
|
106
|
-
|
|
150
|
+
|
|
151
|
+
this.ensurePackageHandlerInitialized();
|
|
152
|
+
return this.packageHandler!.checkPremiumStatusFromInfo(customerInfo);
|
|
107
153
|
}
|
|
108
154
|
|
|
109
155
|
async reset(): Promise<void> {
|
|
110
|
-
if (this.serviceInstance)
|
|
156
|
+
if (this.serviceInstance) {
|
|
157
|
+
await this.serviceInstance.reset();
|
|
158
|
+
}
|
|
159
|
+
|
|
111
160
|
this.state.reset();
|
|
112
161
|
this.serviceInstance = null;
|
|
113
162
|
}
|
|
114
163
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
164
|
+
isConfigured(): boolean {
|
|
165
|
+
return this.managerConfig !== null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
isInitialized(): boolean {
|
|
169
|
+
if (!this.serviceInstance) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return this.serviceInstance.isInitialized();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getEntitlementId(): string {
|
|
177
|
+
if (!this.managerConfig) {
|
|
178
|
+
throw new Error("SubscriptionManager not configured");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return this.managerConfig.config.entitlementIdentifier;
|
|
182
|
+
}
|
|
119
183
|
}
|
|
120
184
|
|
|
121
185
|
export const SubscriptionManager = new SubscriptionManagerImpl();
|
|
@@ -15,14 +15,16 @@ export async function handleRestore(deps: RestoreHandlerDeps, userId: string): P
|
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
17
|
const customerInfo = await Purchases.restorePurchases();
|
|
18
|
-
const
|
|
18
|
+
const entitlement = customerInfo.entitlements.active[deps.config.entitlementIdentifier];
|
|
19
|
+
const isPremium = !!entitlement;
|
|
20
|
+
const productId = entitlement?.productIdentifier ?? null;
|
|
19
21
|
|
|
20
22
|
if (isPremium) {
|
|
21
23
|
await syncPremiumStatus(deps.config, userId, customerInfo);
|
|
22
24
|
}
|
|
23
25
|
await notifyRestoreCompleted(deps.config, userId, isPremium, customerInfo);
|
|
24
26
|
|
|
25
|
-
return { success: true, isPremium, customerInfo };
|
|
27
|
+
return { success: true, isPremium, productId, customerInfo };
|
|
26
28
|
} catch (error) {
|
|
27
29
|
throw new RevenueCatRestoreError(getErrorMessage(error, "Restore failed"));
|
|
28
30
|
}
|
|
@@ -19,8 +19,7 @@ const configurationState = {
|
|
|
19
19
|
configurationPromise: null as Promise<ReturnType<typeof initializeSDK>> | null,
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
// Simple lock mechanism to prevent concurrent configurations
|
|
23
|
-
let configurationLocks = new Set<string>();
|
|
22
|
+
// Simple lock mechanism to prevent concurrent configurations (implementation deferred)
|
|
24
23
|
|
|
25
24
|
function configureLogHandler(): void {
|
|
26
25
|
if (configurationState.isLogHandlerConfigured) return;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { CustomerInfo } from "react-native-purchases";
|
|
8
|
-
import { detectPackageType
|
|
8
|
+
import { detectPackageType } from "../../../../utils/packageTypeDetector";
|
|
9
9
|
|
|
10
10
|
export interface RenewalState {
|
|
11
11
|
previousExpirationDate: string | null;
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
import React, { useMemo } from "react";
|
|
7
7
|
import { View, StyleSheet } from "react-native";
|
|
8
8
|
import { useAppDesignTokens, AtomicText } from "@umituz/react-native-design-system";
|
|
9
|
-
import {
|
|
10
|
-
SUBSCRIPTION_STATUS,
|
|
11
|
-
type SubscriptionStatusType
|
|
12
|
-
} from "../../../
|
|
9
|
+
import {
|
|
10
|
+
SUBSCRIPTION_STATUS,
|
|
11
|
+
type SubscriptionStatusType
|
|
12
|
+
} from "../../../core/SubscriptionConstants";
|
|
13
|
+
|
|
14
|
+
export type { SubscriptionStatusType };
|
|
13
15
|
|
|
14
16
|
export interface PremiumStatusBadgeProps {
|
|
15
17
|
status: SubscriptionStatusType;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { View, TouchableOpacity, TextInput } from "react-native";
|
|
3
3
|
import { AtomicText, BaseModal, useAppDesignTokens } from "@umituz/react-native-design-system";
|
|
4
|
-
import { usePaywallFeedback } from "
|
|
4
|
+
import { usePaywallFeedback } from "../../../../../presentation/hooks/feedback/usePaywallFeedback";
|
|
5
5
|
import { createPaywallFeedbackStyles } from "./paywallFeedbackStyles";
|
|
6
6
|
|
|
7
7
|
const FEEDBACK_OPTION_IDS = [
|
package/src/{presentation → domains/subscription/presentation}/types/SubscriptionDetailTypes.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Type definitions for subscription detail screen and components
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { SubscriptionStatusType } from "../../
|
|
6
|
+
import type { SubscriptionStatusType } from "../../core/SubscriptionStatus";
|
|
7
7
|
import type { CreditInfo } from "../components/details/PremiumDetailsCardTypes";
|
|
8
8
|
|
|
9
9
|
export type { SubscriptionStatusType, CreditInfo };
|
|
@@ -16,6 +16,8 @@ export interface SubscriptionDetailTranslations {
|
|
|
16
16
|
statusExpired: string;
|
|
17
17
|
statusInactive: string;
|
|
18
18
|
statusCanceled: string;
|
|
19
|
+
/** Free status label */
|
|
20
|
+
statusFree: string;
|
|
19
21
|
/** Trial status label (defaults to statusActive if not provided) */
|
|
20
22
|
statusTrial?: string;
|
|
21
23
|
/** Trial canceled status label (defaults to statusCanceled if not provided) */
|
|
@@ -109,7 +111,7 @@ export interface SubscriptionHeaderProps {
|
|
|
109
111
|
| "expiresLabel"
|
|
110
112
|
| "purchasedLabel"
|
|
111
113
|
| "lifetimeLabel"
|
|
112
|
-
|
|
114
|
+
> & { statusFree: string };
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
/** Props for credits list component */
|
package/src/{presentation → domains/subscription/presentation}/types/SubscriptionSettingsTypes.ts
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Type definitions for subscription settings configuration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { SubscriptionStatusType } from "../../
|
|
6
|
+
import type { SubscriptionStatusType } from "../../core/SubscriptionConstants";
|
|
7
7
|
import type {
|
|
8
8
|
SubscriptionDetailConfig,
|
|
9
9
|
UpgradePromptConfig,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { getSavedPurchase, clearSavedPurchase } from "./useAuthAwarePurchase";
|
|
13
13
|
import { usePremium } from "./usePremium";
|
|
14
14
|
import { SubscriptionManager } from "../infrastructure/managers/SubscriptionManager";
|
|
15
|
-
import { usePurchaseLoadingStore } from "
|
|
15
|
+
import { usePurchaseLoadingStore } from "./stores";
|
|
16
16
|
|
|
17
17
|
export interface UseSavedPurchaseAutoExecutionParams {
|
|
18
18
|
onSuccess?: () => void;
|
|
@@ -7,21 +7,21 @@ import { useMemo, useCallback } from "react";
|
|
|
7
7
|
import { useCredits } from "../../credits/presentation/useCredits";
|
|
8
8
|
import { usePaywallVisibility } from "./usePaywallVisibility";
|
|
9
9
|
import { calculateDaysRemaining } from "../core/SubscriptionStatus";
|
|
10
|
-
import { formatDate } from "
|
|
10
|
+
import { formatDate } from "./utils/subscriptionDateUtils";
|
|
11
11
|
import { useCreditsArray, getSubscriptionStatusType } from "./useSubscriptionSettingsConfig.utils";
|
|
12
12
|
import { getCreditsConfig } from "../../credits/infrastructure/CreditsRepositoryProvider";
|
|
13
13
|
import type {
|
|
14
14
|
SubscriptionSettingsConfig,
|
|
15
15
|
SubscriptionStatusType,
|
|
16
16
|
UseSubscriptionSettingsConfigParams,
|
|
17
|
-
} from "
|
|
17
|
+
} from "./types/SubscriptionSettingsTypes";
|
|
18
18
|
|
|
19
19
|
export type {
|
|
20
20
|
SubscriptionSettingsConfig,
|
|
21
21
|
SubscriptionSettingsItemConfig,
|
|
22
22
|
SubscriptionSettingsTranslations,
|
|
23
23
|
UseSubscriptionSettingsConfigParams,
|
|
24
|
-
} from "
|
|
24
|
+
} from "./types/SubscriptionSettingsTypes";
|
|
25
25
|
|
|
26
26
|
export const useSubscriptionSettingsConfig = (
|
|
27
27
|
params: Omit<UseSubscriptionSettingsConfigParams, 'userId'>
|
|
@@ -95,6 +95,7 @@ export const useSubscriptionSettingsConfig = (
|
|
|
95
95
|
statusExpired: translations.statusExpired,
|
|
96
96
|
statusInactive: translations.statusInactive,
|
|
97
97
|
statusCanceled: translations.statusCanceled,
|
|
98
|
+
statusFree: translations.statusInactive,
|
|
98
99
|
expiresLabel: translations.expiresLabel,
|
|
99
100
|
purchasedLabel: translations.purchasedLabel,
|
|
100
101
|
lifetimeLabel: translations.lifetimeLabel,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { useMemo } from "react";
|
|
7
7
|
import type { UserCredits } from "../../credits/core/Credits";
|
|
8
8
|
import { resolveSubscriptionStatus, type PeriodType, type SubscriptionStatusType } from "../core/SubscriptionStatus";
|
|
9
|
-
import type { SubscriptionSettingsTranslations } from "
|
|
9
|
+
import type { SubscriptionSettingsTranslations } from "./types/SubscriptionSettingsTypes";
|
|
10
10
|
|
|
11
11
|
export interface CreditsInfo {
|
|
12
12
|
id: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { doc, getDoc, setDoc, serverTimestamp,
|
|
1
|
+
import { doc, getDoc, setDoc, serverTimestamp, type Firestore } from "firebase/firestore";
|
|
2
2
|
import { getFirestore } from "@umituz/react-native-firebase";
|
|
3
|
-
import type { DeviceTrialRecord } from "
|
|
3
|
+
import type { DeviceTrialRecord } from "../core/TrialTypes";
|
|
4
4
|
|
|
5
5
|
const DEVICE_TRIALS_COLLECTION = "device_trials";
|
|
6
6
|
|
package/src/index.ts
CHANGED
|
@@ -54,11 +54,11 @@ export {
|
|
|
54
54
|
export * from "./presentation/hooks";
|
|
55
55
|
|
|
56
56
|
// Presentation Layer - Components
|
|
57
|
-
export * from "./presentation/components/details/PremiumDetailsCard";
|
|
58
|
-
export * from "./presentation/components/details/PremiumStatusBadge";
|
|
59
|
-
export * from "./presentation/components/sections/SubscriptionSection";
|
|
60
|
-
export * from "./presentation/components/feedback/PaywallFeedbackModal";
|
|
61
|
-
export * from "./presentation/screens/SubscriptionDetailScreen";
|
|
57
|
+
export * from "./domains/subscription/presentation/components/details/PremiumDetailsCard";
|
|
58
|
+
export * from "./domains/subscription/presentation/components/details/PremiumStatusBadge";
|
|
59
|
+
export * from "./domains/subscription/presentation/components/sections/SubscriptionSection";
|
|
60
|
+
export * from "./domains/subscription/presentation/components/feedback/PaywallFeedbackModal";
|
|
61
|
+
export * from "./domains/subscription/presentation/screens/SubscriptionDetailScreen";
|
|
62
62
|
export * from "./domains/paywall/components/PaywallContainer";
|
|
63
63
|
|
|
64
64
|
export type {
|
|
@@ -22,10 +22,13 @@ export class SubscriptionEventBus {
|
|
|
22
22
|
this.listeners[event] = [];
|
|
23
23
|
}
|
|
24
24
|
this.listeners[event].push(callback);
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
// Return unsubscribe function
|
|
27
27
|
return () => {
|
|
28
|
-
|
|
28
|
+
const listeners = this.listeners[event];
|
|
29
|
+
if (listeners) {
|
|
30
|
+
this.listeners[event] = listeners.filter(l => l !== callback);
|
|
31
|
+
}
|
|
29
32
|
};
|
|
30
33
|
}
|
|
31
34
|
|
package/src/utils/tierUtils.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Core logic for determining user tier and premium status
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { USER_TIER, type UserTierInfo } from './types';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
export function getUserTierInfo(
|
|
@@ -14,7 +14,7 @@ export function getUserTierInfo(
|
|
|
14
14
|
): UserTierInfo {
|
|
15
15
|
if (isAnonymous || userId === null) {
|
|
16
16
|
return {
|
|
17
|
-
tier:
|
|
17
|
+
tier: USER_TIER.ANONYMOUS,
|
|
18
18
|
isPremium: false,
|
|
19
19
|
isAnonymous: true,
|
|
20
20
|
isAuthenticated: false,
|
|
@@ -23,7 +23,7 @@ export function getUserTierInfo(
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
|
-
tier: isPremium ?
|
|
26
|
+
tier: isPremium ? USER_TIER.PREMIUM : USER_TIER.FREEMIUM,
|
|
27
27
|
isPremium,
|
|
28
28
|
isAnonymous: false,
|
|
29
29
|
isAuthenticated: true,
|
package/src/utils/types.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Type definitions for user tier system
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { USER_TIER, type UserTierType } from '../domains/subscription/core/SubscriptionConstants';
|
|
8
|
+
|
|
9
|
+
export type UserTier = UserTierType;
|
|
10
|
+
export { USER_TIER };
|
|
8
11
|
|
|
9
12
|
export interface UserTierInfo {
|
|
10
13
|
/** User tier classification */
|
package/src/utils/validation.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Type guards and validation functions for user tier system
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { USER_TIER, type UserTier, type UserTierInfo } from './types';
|
|
8
8
|
|
|
9
9
|
export function isValidUserTier(value: unknown): value is UserTier {
|
|
10
|
-
return value ===
|
|
10
|
+
return value === USER_TIER.ANONYMOUS || value === USER_TIER.FREEMIUM || value === USER_TIER.PREMIUM;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function isUserTierInfo(value: unknown): value is UserTierInfo {
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# Config Domain
|
|
2
|
-
|
|
3
|
-
Central configuration system for subscription plans, product configurations, and package management.
|
|
4
|
-
|
|
5
|
-
## Location
|
|
6
|
-
|
|
7
|
-
- **Base Path**: `/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/config/`
|
|
8
|
-
- **Domain**: `src/domains/config/domain/`
|
|
9
|
-
- **Entities**: `src/domains/config/domain/entities/`
|
|
10
|
-
|
|
11
|
-
## Strategy
|
|
12
|
-
|
|
13
|
-
### Plan Management
|
|
14
|
-
|
|
15
|
-
Comprehensive subscription plan configuration system.
|
|
16
|
-
|
|
17
|
-
- **Plan Types**: Monthly, annual, and lifetime plan configurations
|
|
18
|
-
- **Product Metadata**: RevenueCat product metadata management
|
|
19
|
-
- **Validation**: Configuration validation and type safety
|
|
20
|
-
- **Helper Functions**: Plan comparison and filtering utilities
|
|
21
|
-
|
|
22
|
-
### Configuration Objects
|
|
23
|
-
|
|
24
|
-
Structured configuration for different aspects of the system.
|
|
25
|
-
|
|
26
|
-
- **SubscriptionConfig**: Main subscription configuration
|
|
27
|
-
- **WalletConfig**: Credit system configuration
|
|
28
|
-
- **Plan Entities**: Individual plan definitions
|
|
29
|
-
- **Validation Rules**: Configuration validation schemas
|
|
30
|
-
|
|
31
|
-
### Helper Utilities
|
|
32
|
-
|
|
33
|
-
Plan comparison and manipulation functions.
|
|
34
|
-
|
|
35
|
-
- **Plan Comparison**: Value comparison between plans
|
|
36
|
-
- **Price Formatting**: Currency-aware price formatting
|
|
37
|
-
- **Discount Calculation**: Savings and discount calculations
|
|
38
|
-
- **Package Filtering**: Type-based package filtering
|
|
39
|
-
|
|
40
|
-
### Validation System
|
|
41
|
-
|
|
42
|
-
Comprehensive configuration validation.
|
|
43
|
-
|
|
44
|
-
- **Plan Validation**: Plan entity validation rules
|
|
45
|
-
- **Config Validation**: Complete configuration validation
|
|
46
|
-
- **Type Safety**: TypeScript type definitions
|
|
47
|
-
- **Error Messages**: Detailed validation error reporting
|
|
48
|
-
|
|
49
|
-
## Restrictions
|
|
50
|
-
|
|
51
|
-
### REQUIRED
|
|
52
|
-
|
|
53
|
-
- **Type Safety**: Always use TypeScript type definitions
|
|
54
|
-
- **Validation**: Validate configurations before runtime use
|
|
55
|
-
- **Default Values**: Provide meaningful default values
|
|
56
|
-
- **Immutable Updates**: Create new copies instead of modifying
|
|
57
|
-
|
|
58
|
-
### PROHIBITED
|
|
59
|
-
|
|
60
|
-
- **Invalid Prices**: Negative or zero prices not allowed
|
|
61
|
-
- **Missing IDs**: All plans must have valid IDs
|
|
62
|
-
- **Duplicate Plans**: No duplicate plan IDs allowed
|
|
63
|
-
- **Hardcoded Values**: Use configuration system
|
|
64
|
-
|
|
65
|
-
### CRITICAL
|
|
66
|
-
|
|
67
|
-
- **Configuration Integrity**: All configurations must be valid
|
|
68
|
-
- **Plan Consistency**: Related plans must be consistent
|
|
69
|
-
- **Currency Handling**: Proper currency code usage
|
|
70
|
-
- **Feature Lists**: Accurate feature mapping
|
|
71
|
-
|
|
72
|
-
## AI Agent Guidelines
|
|
73
|
-
|
|
74
|
-
### When Modifying Configuration System
|
|
75
|
-
|
|
76
|
-
1. **Type Definitions**: Update TypeScript types for new config
|
|
77
|
-
2. **Validation Rules**: Add validation for new fields
|
|
78
|
-
3. **Default Values**: Provide sensible defaults
|
|
79
|
-
4. **Documentation**: Document configuration options
|
|
80
|
-
|
|
81
|
-
### When Adding New Plan Types
|
|
82
|
-
|
|
83
|
-
1. **Entity Pattern**: Follow existing entity patterns
|
|
84
|
-
2. **Validation**: Add validation rules
|
|
85
|
-
3. **Helper Functions**: Create helper functions
|
|
86
|
-
4. **Testing**: Test with various configurations
|
|
87
|
-
|
|
88
|
-
### When Fixing Configuration Bugs
|
|
89
|
-
|
|
90
|
-
1. **Validation Logic**: Check validation rules
|
|
91
|
-
2. **Type Definitions**: Verify type correctness
|
|
92
|
-
3. **Default Values**: Ensure proper defaults
|
|
93
|
-
4. **Edge Cases**: Test boundary conditions
|
|
94
|
-
|
|
95
|
-
## Related Documentation
|
|
96
|
-
|
|
97
|
-
- [Paywall Domain](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/paywall/README.md)
|
|
98
|
-
- [Wallet Domain](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domains/wallet/README.md)
|
|
99
|
-
- [RevenueCat Integration](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/revenuecat/README.md)
|
|
100
|
-
- [Domain Layer](/Users/umituz/Desktop/github/umituz/apps/artificial_intelligence/npm-packages/react-native-subscription/src/domain/README.md)
|