@umituz/react-native-subscription 2.37.39 → 2.37.41
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/CreditLimitCalculator.ts +1 -9
- package/src/domains/credits/application/DeductCreditsCommand.ts +16 -7
- package/src/domains/credits/application/RefundCreditsCommand.ts +1 -5
- package/src/domains/credits/application/credit-strategies/TrialCreditStrategy.ts +1 -5
- package/src/domains/credits/application/creditDocumentHelpers.ts +2 -9
- package/src/domains/credits/core/Credits.ts +0 -23
- package/src/domains/credits/core/CreditsMapper.ts +0 -6
- package/src/domains/credits/core/UserCreditsDocument.ts +0 -12
- package/src/domains/credits/infrastructure/CreditsRepositoryManager.ts +0 -21
- package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +2 -1
- package/src/domains/credits/presentation/deduct-credit/useDeductCredit.ts +2 -2
- package/src/domains/credits/presentation/useCredits.ts +10 -9
- package/src/domains/paywall/components/PaywallContainer.types.ts +0 -28
- package/src/domains/paywall/components/PaywallModal.styles.ts +0 -4
- package/src/domains/paywall/entities/types.ts +0 -5
- package/src/domains/paywall/hooks/usePaywallActions.ts +1 -15
- package/src/domains/revenuecat/core/errors/RevenueCatError.ts +0 -6
- package/src/domains/revenuecat/core/errors/RevenueCatErrorHandler.ts +0 -24
- package/src/domains/revenuecat/core/errors/RevenueCatErrorMessages.ts +0 -18
- package/src/domains/revenuecat/core/errors/index.ts +0 -4
- package/src/domains/revenuecat/core/types/RevenueCatConfig.ts +3 -7
- package/src/domains/revenuecat/core/types/RevenueCatData.ts +4 -9
- package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +5 -65
- package/src/domains/revenuecat/core/types/index.ts +0 -4
- package/src/domains/revenuecat/infrastructure/services/UserSwitchMutex.ts +1 -24
- package/src/domains/subscription/application/SubscriptionAuthListener.ts +5 -21
- package/src/domains/subscription/application/SubscriptionInitializerTypes.ts +1 -5
- package/src/domains/subscription/application/SubscriptionSyncProcessor.ts +6 -2
- package/src/domains/subscription/application/SubscriptionSyncService.ts +11 -2
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
- package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +15 -2
- package/src/domains/subscription/application/initializer/ServiceConfigurator.ts +9 -2
- package/src/domains/subscription/constants/thresholds.ts +0 -9
- package/src/domains/subscription/core/SubscriptionConstants.ts +0 -4
- package/src/domains/subscription/core/SubscriptionStatus.ts +11 -21
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +4 -7
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/subscriptionQueryKeys.ts +0 -13
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +0 -18
- package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +3 -17
- package/src/domains/subscription/infrastructure/hooks/useRevenueCatTrialEligibility.ts +0 -17
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionPackages.ts +0 -19
- package/src/domains/subscription/infrastructure/hooks/useSubscriptionQueries.ts +0 -6
- package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +0 -17
- package/src/domains/subscription/infrastructure/state/initializationState.ts +0 -25
- package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +0 -21
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +2 -7
- package/src/domains/subscription/infrastructure/utils/authPurchaseState.ts +19 -6
- package/src/domains/subscription/infrastructure/utils/renewal/PackageTierComparator.ts +1 -0
- package/src/domains/subscription/infrastructure/utils/trialEligibilityUtils.ts +0 -18
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.styles.ts +0 -5
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCardTypes.ts +0 -5
- package/src/domains/subscription/presentation/components/feedback/paywallFeedbackStyles.ts +0 -5
- package/src/domains/subscription/presentation/stores/index.ts +0 -4
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +0 -13
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +35 -21
- package/src/domains/subscription/presentation/usePaywallVisibility.ts +0 -9
- package/src/domains/subscription/presentation/useSubscriptionStatus.ts +8 -11
- package/src/domains/subscription/utils/authGuards.ts +3 -0
- package/src/domains/trial/application/TrialService.ts +0 -9
- package/src/domains/trial/core/TrialTypes.ts +0 -8
- package/src/domains/wallet/domain/mappers/TransactionMapper.ts +0 -5
- package/src/domains/wallet/domain/types/transaction.types.ts +0 -7
- package/src/domains/wallet/index.ts +0 -7
- package/src/domains/wallet/infrastructure/config/walletConfig.ts +0 -11
- package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +6 -3
- package/src/domains/wallet/presentation/hooks/useWallet.ts +0 -7
- package/src/domains/wallet/utils/transactionIconMap.ts +0 -10
- package/src/global.d.ts +0 -6
- package/src/index.ts +1 -4
- package/src/init/createSubscriptionInitModule.ts +12 -2
- package/src/init/index.ts +1 -5
- package/src/presentation/hooks/feedback/useFeedbackSubmit.ts +0 -11
- package/src/shared/application/FeedbackService.ts +3 -21
- package/src/shared/application/ports/ISubscriptionRepository.ts +0 -4
- package/src/shared/infrastructure/SubscriptionEventBus.ts +0 -13
- package/src/shared/infrastructure/firestore/collectionUtils.ts +1 -17
- package/src/shared/infrastructure/firestore/index.ts +0 -4
- package/src/shared/infrastructure/firestore/resultUtils.ts +0 -12
- package/src/shared/infrastructure/react-query/hooks/usePreviousUserCleanup.ts +0 -17
- package/src/shared/infrastructure/react-query/queryConfig.ts +0 -15
- package/src/shared/utils/BaseError.ts +0 -5
- package/src/shared/utils/Result.ts +0 -20
- package/src/shared/utils/dateConverter.ts +6 -46
- package/src/utils/appUtils.ts +0 -16
- package/src/utils/creditMapper.ts +0 -7
- package/src/utils/dateUtils.compare.ts +0 -24
- package/src/utils/dateUtils.core.ts +0 -39
- package/src/utils/dateUtils.format.ts +0 -41
- package/src/utils/dateUtils.math.ts +0 -41
- package/src/utils/dateUtils.ts +0 -5
- package/src/utils/packagePeriodUtils.ts +0 -20
- package/src/utils/packageTypeDetector.ts +1 -21
- package/src/utils/premiumStatusUtils.ts +1 -14
- package/src/utils/priceUtils.ts +0 -35
- package/src/utils/tierUtils.ts +1 -8
- package/src/utils/types.ts +1 -25
- package/src/utils/validation.ts +1 -7
- package/src/domains/README.md +0 -52
- package/src/domains/config/domain/README.md +0 -37
- package/src/domains/config/domain/entities/README.md +0 -41
- package/src/domains/paywall/README.md +0 -101
- package/src/domains/paywall/entities/README.md +0 -40
- package/src/domains/paywall/hooks/README.md +0 -41
- package/src/domains/subscription/application/syncConstants.ts +0 -1
- package/src/domains/subscription/infrastructure/README.md +0 -41
- package/src/domains/subscription/infrastructure/config/README.md +0 -49
- package/src/domains/subscription/infrastructure/handlers/README.md +0 -41
- package/src/domains/subscription/infrastructure/hooks/README.md +0 -50
- package/src/domains/subscription/infrastructure/managers/README.md +0 -41
- package/src/domains/subscription/infrastructure/services/README.md +0 -42
- package/src/domains/subscription/infrastructure/utils/README.md +0 -41
- package/src/domains/subscription/presentation/components/README.md +0 -155
- package/src/domains/subscription/presentation/components/details/CreditRow.md +0 -92
- package/src/domains/subscription/presentation/components/details/DetailRow.md +0 -91
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.md +0 -93
- package/src/domains/subscription/presentation/components/details/PremiumStatusBadge.md +0 -91
- package/src/domains/subscription/presentation/components/details/README.md +0 -99
- package/src/domains/subscription/presentation/components/feedback/PaywallFeedbackModal.md +0 -90
- package/src/domains/subscription/presentation/components/feedback/README.md +0 -99
- package/src/domains/subscription/presentation/components/paywall/PaywallModal.md +0 -94
- package/src/domains/subscription/presentation/components/paywall/README.md +0 -54
- package/src/domains/subscription/presentation/components/sections/README.md +0 -99
- package/src/domains/subscription/presentation/components/sections/SubscriptionSection.md +0 -94
- package/src/domains/subscription/presentation/utils/README.md +0 -31
- package/src/domains/wallet/README.md +0 -51
- package/src/domains/wallet/domain/README.md +0 -41
- package/src/domains/wallet/infrastructure/README.md +0 -41
- package/src/domains/wallet/presentation/components/README.md +0 -41
- package/src/domains/wallet/presentation/hooks/README.md +0 -41
- package/src/shared/application/ports/README.md +0 -48
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Paywall Visibility Hook
|
|
3
|
-
* Simple global state for paywall visibility using module-level state
|
|
4
|
-
* Generic implementation for 100+ apps
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import { useCallback, useSyncExternalStore } from "react";
|
|
8
2
|
import type { PurchaseSource } from "../core/SubscriptionConstants";
|
|
9
3
|
|
|
@@ -29,9 +23,6 @@ const setPaywallState = (visible: boolean, source?: PurchaseSource): void => {
|
|
|
29
23
|
listeners.forEach((listener) => listener());
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
/**
|
|
33
|
-
* Direct paywall control for non-React services (e.g., appInitializer)
|
|
34
|
-
*/
|
|
35
26
|
export const paywallControl = {
|
|
36
27
|
open: (source?: PurchaseSource) => setPaywallState(true, source),
|
|
37
28
|
close: () => setPaywallState(false, undefined),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useQuery, useQueryClient } from "@umituz/react-native-design-system";
|
|
2
2
|
import { useEffect, useSyncExternalStore } from "react";
|
|
3
|
-
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
3
|
+
import { useAuthStore, selectUserId, selectIsAnonymous } from "@umituz/react-native-auth";
|
|
4
4
|
import { SubscriptionManager } from "../infrastructure/managers/SubscriptionManager";
|
|
5
5
|
import { initializationState } from "../infrastructure/state/initializationState";
|
|
6
6
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../shared/infrastructure/SubscriptionEventBus";
|
|
7
7
|
import { SubscriptionStatusResult } from "./useSubscriptionStatus.types";
|
|
8
|
-
import {
|
|
8
|
+
import { isRegisteredUser } from "../utils/authGuards";
|
|
9
9
|
import { NO_CACHE_QUERY_CONFIG } from "../../../shared/infrastructure/react-query/queryConfig";
|
|
10
10
|
import { usePreviousUserCleanup } from "../../../shared/infrastructure/react-query/hooks/usePreviousUserCleanup";
|
|
11
11
|
|
|
@@ -17,32 +17,30 @@ export const subscriptionStatusQueryKeys = {
|
|
|
17
17
|
|
|
18
18
|
export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
19
19
|
const userId = useAuthStore(selectUserId);
|
|
20
|
+
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
20
21
|
const queryClient = useQueryClient();
|
|
21
22
|
const isConfigured = SubscriptionManager.isConfigured();
|
|
23
|
+
const isUserRegistered = isRegisteredUser(userId, isAnonymous);
|
|
22
24
|
|
|
23
|
-
// Reactive initialization state - triggers re-render when BackgroundInitializer completes
|
|
24
25
|
const initState = useSyncExternalStore(
|
|
25
26
|
initializationState.subscribe,
|
|
26
27
|
initializationState.getSnapshot,
|
|
27
28
|
initializationState.getSnapshot,
|
|
28
29
|
);
|
|
29
30
|
|
|
30
|
-
// Check if initialized for this specific user (reactive)
|
|
31
31
|
const isInitialized = userId
|
|
32
32
|
? initState.initialized && initState.userId === userId
|
|
33
33
|
: false;
|
|
34
34
|
|
|
35
|
-
const queryEnabled =
|
|
35
|
+
const queryEnabled = isUserRegistered && isConfigured && isInitialized;
|
|
36
36
|
|
|
37
37
|
const { data, status, error, refetch } = useQuery({
|
|
38
38
|
queryKey: subscriptionStatusQueryKeys.user(userId),
|
|
39
39
|
queryFn: async () => {
|
|
40
|
-
if (!
|
|
40
|
+
if (!isUserRegistered) {
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
// No side effects - just check premium status
|
|
45
|
-
// Initialization is handled by BackgroundInitializer
|
|
46
44
|
try {
|
|
47
45
|
const result = await SubscriptionManager.checkPremiumStatus();
|
|
48
46
|
return result;
|
|
@@ -54,11 +52,10 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
54
52
|
...NO_CACHE_QUERY_CONFIG,
|
|
55
53
|
});
|
|
56
54
|
|
|
57
|
-
// Clean up previous user's cache on logout/user switch
|
|
58
55
|
usePreviousUserCleanup(userId, queryClient, subscriptionStatusQueryKeys.user);
|
|
59
56
|
|
|
60
57
|
useEffect(() => {
|
|
61
|
-
if (!
|
|
58
|
+
if (!isUserRegistered) return undefined;
|
|
62
59
|
|
|
63
60
|
const unsubscribe = subscriptionEventBus.on(
|
|
64
61
|
SUBSCRIPTION_EVENTS.PREMIUM_STATUS_CHANGED,
|
|
@@ -72,7 +69,7 @@ export const useSubscriptionStatus = (): SubscriptionStatusResult => {
|
|
|
72
69
|
);
|
|
73
70
|
|
|
74
71
|
return unsubscribe;
|
|
75
|
-
}, [userId, queryClient]);
|
|
72
|
+
}, [userId, isUserRegistered, queryClient]);
|
|
76
73
|
|
|
77
74
|
const isLoading = status === "pending";
|
|
78
75
|
|
|
@@ -4,3 +4,6 @@ export function isAuthenticated(userId: string | null | undefined): userId is st
|
|
|
4
4
|
return isDefined(userId) && userId.length > 0;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
export function isRegisteredUser(userId: string | null | undefined, isAnonymous: boolean): userId is string {
|
|
8
|
+
return isAuthenticated(userId) && !isAnonymous;
|
|
9
|
+
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trial Service - Facade for device-based trial tracking
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import { arrayUnion, type FieldValue } from "firebase/firestore";
|
|
6
2
|
import { serverTimestamp } from "@umituz/react-native-firebase";
|
|
7
3
|
import { PersistentDeviceIdService } from "@umituz/react-native-design-system";
|
|
@@ -10,7 +6,6 @@ import { TrialEligibilityService } from "./TrialEligibilityService";
|
|
|
10
6
|
import type { TrialEligibilityResult } from "../core/TrialTypes";
|
|
11
7
|
export type { TrialEligibilityResult };
|
|
12
8
|
|
|
13
|
-
// Type for Firestore write operations with FieldValue
|
|
14
9
|
interface TrialRecordWrite {
|
|
15
10
|
deviceId?: string;
|
|
16
11
|
hasUsedTrial?: boolean;
|
|
@@ -26,10 +21,6 @@ const repository = new DeviceTrialRepository();
|
|
|
26
21
|
|
|
27
22
|
export const getDeviceId = () => PersistentDeviceIdService.getDeviceId();
|
|
28
23
|
|
|
29
|
-
/**
|
|
30
|
-
* Ensures a valid device ID is available
|
|
31
|
-
* Uses provided deviceId if non-empty, otherwise fetches from PersistentDeviceIdService
|
|
32
|
-
*/
|
|
33
24
|
async function ensureDeviceId(deviceId?: string): Promise<string> {
|
|
34
25
|
return (deviceId && deviceId.length > 0) ? deviceId : await getDeviceId();
|
|
35
26
|
}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Trial Types and Constants
|
|
3
|
-
* Device-based trial tracking types
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** Trial constants */
|
|
7
1
|
export const TRIAL_CONFIG = {
|
|
8
2
|
DURATION_DAYS: 3,
|
|
9
3
|
CREDITS: 0,
|
|
10
4
|
} as const;
|
|
11
5
|
|
|
12
|
-
/** Device trial record in Firestore */
|
|
13
6
|
export interface DeviceTrialRecord {
|
|
14
7
|
deviceId: string;
|
|
15
8
|
hasUsedTrial: boolean;
|
|
@@ -23,7 +16,6 @@ export interface DeviceTrialRecord {
|
|
|
23
16
|
updatedAt: Date;
|
|
24
17
|
}
|
|
25
18
|
|
|
26
|
-
/** Trial eligibility result */
|
|
27
19
|
export interface TrialEligibilityResult {
|
|
28
20
|
eligible: boolean;
|
|
29
21
|
reason?: "already_used" | "device_not_found" | "error" | "user_already_used";
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wallet Configuration
|
|
3
|
-
*
|
|
4
|
-
* Global configuration for wallet feature.
|
|
5
|
-
* Set once at app init, used by WalletScreen automatically.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import type { WalletScreenTranslations } from "../../presentation/screens/WalletScreen.types";
|
|
9
2
|
|
|
10
|
-
|
|
11
3
|
interface WalletConfiguration {
|
|
12
4
|
translations: WalletScreenTranslations;
|
|
13
5
|
transactionCollection: string;
|
|
@@ -41,9 +33,6 @@ const DEFAULT_CONFIG: WalletConfiguration = {
|
|
|
41
33
|
|
|
42
34
|
let walletConfig: WalletConfiguration = { ...DEFAULT_CONFIG };
|
|
43
35
|
|
|
44
|
-
/**
|
|
45
|
-
* Get current wallet configuration
|
|
46
|
-
*/
|
|
47
36
|
export function getWalletConfig(): WalletConfiguration {
|
|
48
37
|
return walletConfig;
|
|
49
38
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useQuery } from "@umituz/react-native-design-system";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
3
|
+
import { useAuthStore, selectUserId, selectIsAnonymous } from "@umituz/react-native-auth";
|
|
4
4
|
import { NO_CACHE_QUERY_CONFIG } from "../../../../shared/infrastructure/react-query/queryConfig";
|
|
5
5
|
import type {
|
|
6
6
|
CreditLog,
|
|
@@ -31,16 +31,19 @@ export function useTransactionHistory({
|
|
|
31
31
|
limit = 50,
|
|
32
32
|
}: UseTransactionHistoryParams): UseTransactionHistoryResult {
|
|
33
33
|
const userId = useAuthStore(selectUserId);
|
|
34
|
+
const isAnonymous = useAuthStore(selectIsAnonymous);
|
|
34
35
|
|
|
35
36
|
const repository = useMemo(
|
|
36
37
|
() => new TransactionRepository(config),
|
|
37
38
|
[config]
|
|
38
39
|
);
|
|
39
40
|
|
|
41
|
+
const isUserRegistered = !!userId && !isAnonymous;
|
|
42
|
+
|
|
40
43
|
const { data, isLoading, error, refetch } = useQuery({
|
|
41
44
|
queryKey: [...transactionQueryKeys.user(userId ?? ""), limit],
|
|
42
45
|
queryFn: async () => {
|
|
43
|
-
if (!userId) return [];
|
|
46
|
+
if (!userId || isAnonymous) return [];
|
|
44
47
|
|
|
45
48
|
const result = await repository.getTransactions({
|
|
46
49
|
userId,
|
|
@@ -53,7 +56,7 @@ export function useTransactionHistory({
|
|
|
53
56
|
|
|
54
57
|
return result.data ?? [];
|
|
55
58
|
},
|
|
56
|
-
enabled:
|
|
59
|
+
enabled: isUserRegistered,
|
|
57
60
|
...NO_CACHE_QUERY_CONFIG,
|
|
58
61
|
});
|
|
59
62
|
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useWallet Hook
|
|
3
|
-
*
|
|
4
|
-
* Orchestration hook for wallet functionality.
|
|
5
|
-
* Combines balance, transactions, and purchase state.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import { useCallback, useMemo } from "react";
|
|
9
2
|
import { useCredits } from "../../../credits/presentation/useCredits";
|
|
10
3
|
import {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction Icon Mapping Utility
|
|
3
|
-
* Maps transaction reasons to their corresponding icons
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import type { TransactionReason } from "../domain/types/transaction.types";
|
|
7
2
|
|
|
8
3
|
const ICON_MAP: Record<TransactionReason, string> = {
|
|
@@ -16,11 +11,6 @@ const ICON_MAP: Record<TransactionReason, string> = {
|
|
|
16
11
|
expired: "clock",
|
|
17
12
|
};
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Get icon name for a transaction reason
|
|
21
|
-
* @param reason - Transaction reason type
|
|
22
|
-
* @returns Icon name for the transaction
|
|
23
|
-
*/
|
|
24
14
|
export function getTransactionIcon(reason: TransactionReason): string {
|
|
25
15
|
return ICON_MAP[reason] || "circle";
|
|
26
16
|
}
|
package/src/global.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global type declarations for React Native environment
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** React Native development flag */
|
|
6
1
|
declare const __DEV__: boolean;
|
|
7
2
|
|
|
8
|
-
/** Extend NodeJS namespace for React Native compatibility */
|
|
9
3
|
declare namespace NodeJS {
|
|
10
4
|
interface Global {
|
|
11
5
|
__DEV__: boolean;
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React Native Subscription - Public API
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
// Domain Layer - Constants & Types
|
|
6
2
|
export * from "./domains/subscription/core/SubscriptionConstants";
|
|
7
3
|
export {
|
|
@@ -112,6 +108,7 @@ export type { PurchaseLoadingOverlayProps } from "./domains/subscription/present
|
|
|
112
108
|
// Init Module Factory
|
|
113
109
|
export {
|
|
114
110
|
createSubscriptionInitModule,
|
|
111
|
+
cleanupSubscriptionModule,
|
|
115
112
|
type SubscriptionInitModuleConfig,
|
|
116
113
|
} from './init';
|
|
117
114
|
|
|
@@ -7,6 +7,15 @@ export interface SubscriptionInitModuleConfig extends Omit<SubscriptionInitConfi
|
|
|
7
7
|
dependsOn?: string[];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
let subscriptionCleanup: (() => void) | null = null;
|
|
11
|
+
|
|
12
|
+
export function cleanupSubscriptionModule(): void {
|
|
13
|
+
if (subscriptionCleanup) {
|
|
14
|
+
subscriptionCleanup();
|
|
15
|
+
subscriptionCleanup = null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export function createSubscriptionInitModule(config: SubscriptionInitModuleConfig): InitModule {
|
|
11
20
|
const { getApiKey, critical = false, dependsOn = ['auth'], ...subscriptionConfig } = config;
|
|
12
21
|
|
|
@@ -21,9 +30,10 @@ export function createSubscriptionInitModule(config: SubscriptionInitModuleConfi
|
|
|
21
30
|
return true;
|
|
22
31
|
}
|
|
23
32
|
|
|
24
|
-
await initializeSubscription({ apiKey, ...subscriptionConfig });
|
|
33
|
+
subscriptionCleanup = await initializeSubscription({ apiKey, ...subscriptionConfig });
|
|
25
34
|
return true;
|
|
26
|
-
} catch {
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('[SubscriptionInitModule] Initialization failed:', error instanceof Error ? error.message : String(error));
|
|
27
37
|
return false;
|
|
28
38
|
}
|
|
29
39
|
},
|
package/src/init/index.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feedback Submit Hooks
|
|
3
|
-
* React hooks for submitting feedback to Firestore
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { useCallback } from "react";
|
|
7
2
|
import { useAuth } from "@umituz/react-native-auth";
|
|
8
3
|
import {
|
|
@@ -16,9 +11,6 @@ export interface UsePaywallFeedbackSubmitOptions {
|
|
|
16
11
|
onComplete?: () => void;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Hook for submitting paywall decline feedback
|
|
21
|
-
*/
|
|
22
14
|
export function usePaywallFeedbackSubmit(
|
|
23
15
|
options: UsePaywallFeedbackSubmitOptions = {}
|
|
24
16
|
) {
|
|
@@ -63,9 +55,6 @@ export interface UseSettingsFeedbackSubmitOptions {
|
|
|
63
55
|
onError?: (error: Error) => void;
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
/**
|
|
67
|
-
* Hook for submitting general settings feedback
|
|
68
|
-
*/
|
|
69
58
|
export function useSettingsFeedbackSubmit(
|
|
70
59
|
options: UseSettingsFeedbackSubmitOptions = {}
|
|
71
60
|
) {
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feedback Service
|
|
3
|
-
* Handles feedback submission to Firestore
|
|
4
|
-
* Feedback is stored under users/{userId}/feedback/{feedbackId}
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import { collection, addDoc, doc } from "firebase/firestore";
|
|
8
|
-
import { getFirestore } from "@umituz/react-native-firebase";
|
|
2
|
+
import { getFirestore, serverTimestamp } from "@umituz/react-native-firebase";
|
|
9
3
|
|
|
10
4
|
interface FeedbackData {
|
|
11
5
|
userId: string | null;
|
|
@@ -22,10 +16,6 @@ export interface FeedbackSubmitResult {
|
|
|
22
16
|
error?: Error;
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
/**
|
|
26
|
-
* Submit feedback to Firestore
|
|
27
|
-
* Stores under users/{userId}/feedback
|
|
28
|
-
*/
|
|
29
19
|
async function submitFeedback(
|
|
30
20
|
data: FeedbackData
|
|
31
21
|
): Promise<FeedbackSubmitResult> {
|
|
@@ -40,8 +30,6 @@ async function submitFeedback(
|
|
|
40
30
|
}
|
|
41
31
|
|
|
42
32
|
try {
|
|
43
|
-
const now = new Date().toISOString();
|
|
44
|
-
|
|
45
33
|
const userDocRef = doc(db, "users", data.userId);
|
|
46
34
|
const feedbackCollectionRef = collection(userDocRef, "feedback");
|
|
47
35
|
|
|
@@ -52,8 +40,8 @@ async function submitFeedback(
|
|
|
52
40
|
description: data.description,
|
|
53
41
|
rating: data.rating ?? null,
|
|
54
42
|
status: data.status ?? "pending",
|
|
55
|
-
createdAt:
|
|
56
|
-
updatedAt:
|
|
43
|
+
createdAt: serverTimestamp(),
|
|
44
|
+
updatedAt: serverTimestamp(),
|
|
57
45
|
});
|
|
58
46
|
|
|
59
47
|
return { success: true };
|
|
@@ -65,9 +53,6 @@ async function submitFeedback(
|
|
|
65
53
|
}
|
|
66
54
|
}
|
|
67
55
|
|
|
68
|
-
/**
|
|
69
|
-
* Submit paywall decline feedback
|
|
70
|
-
*/
|
|
71
56
|
export async function submitPaywallFeedback(
|
|
72
57
|
userId: string | null,
|
|
73
58
|
userEmail: string | null,
|
|
@@ -82,9 +67,6 @@ export async function submitPaywallFeedback(
|
|
|
82
67
|
});
|
|
83
68
|
}
|
|
84
69
|
|
|
85
|
-
/**
|
|
86
|
-
* Submit general settings feedback
|
|
87
|
-
*/
|
|
88
70
|
export async function submitSettingsFeedback(
|
|
89
71
|
userId: string | null,
|
|
90
72
|
userEmail: string | null,
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
type EventCallback<T = unknown> = (data: T) => void;
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Simple EventBus Implementation
|
|
5
|
-
* Used to decouple services and provide an observer pattern for subscription events.
|
|
6
|
-
*/
|
|
7
3
|
class SubscriptionEventBus {
|
|
8
4
|
private static instance: SubscriptionEventBus;
|
|
9
5
|
private listeners: Record<string, EventCallback<any>[]> = {};
|
|
@@ -23,13 +19,11 @@ class SubscriptionEventBus {
|
|
|
23
19
|
}
|
|
24
20
|
this.listeners[event].push(callback);
|
|
25
21
|
|
|
26
|
-
// Return unsubscribe function
|
|
27
22
|
return () => {
|
|
28
23
|
const listeners = this.listeners[event];
|
|
29
24
|
if (listeners) {
|
|
30
25
|
this.listeners[event] = listeners.filter(l => l !== callback);
|
|
31
26
|
|
|
32
|
-
// Clean up empty event arrays to prevent memory leak
|
|
33
27
|
if (this.listeners[event].length === 0) {
|
|
34
28
|
delete this.listeners[event];
|
|
35
29
|
}
|
|
@@ -53,10 +47,6 @@ class SubscriptionEventBus {
|
|
|
53
47
|
});
|
|
54
48
|
}
|
|
55
49
|
|
|
56
|
-
/**
|
|
57
|
-
* Clear all listeners for a specific event or all events
|
|
58
|
-
* Useful for cleanup during testing or app state reset
|
|
59
|
-
*/
|
|
60
50
|
clear(event?: string): void {
|
|
61
51
|
if (event) {
|
|
62
52
|
delete this.listeners[event];
|
|
@@ -65,9 +55,6 @@ class SubscriptionEventBus {
|
|
|
65
55
|
}
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
/**
|
|
69
|
-
* Get listener count for debugging
|
|
70
|
-
*/
|
|
71
58
|
getListenerCount(event?: string): number {
|
|
72
59
|
if (event) {
|
|
73
60
|
return this.listeners[event]?.length ?? 0;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Firestore Collection Utilities
|
|
3
|
-
* Shared utilities for building Firestore collection and document references
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { collection, doc } from "firebase/firestore";
|
|
7
2
|
import {
|
|
8
3
|
getFirestore,
|
|
@@ -16,10 +11,6 @@ export interface CollectionConfig {
|
|
|
16
11
|
useUserSubcollection: boolean;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
|
-
/**
|
|
20
|
-
* Build a collection reference based on configuration
|
|
21
|
-
* Supports both root collections and user subcollections
|
|
22
|
-
*/
|
|
23
14
|
export function buildCollectionRef(
|
|
24
15
|
db: Firestore,
|
|
25
16
|
userId: string,
|
|
@@ -31,10 +22,6 @@ export function buildCollectionRef(
|
|
|
31
22
|
return collection(db, config.collectionName);
|
|
32
23
|
}
|
|
33
24
|
|
|
34
|
-
/**
|
|
35
|
-
* Build a document reference based on configuration
|
|
36
|
-
* Supports both root collections and user subcollections
|
|
37
|
-
*/
|
|
38
25
|
export function buildDocRef(
|
|
39
26
|
db: Firestore,
|
|
40
27
|
userId: string,
|
|
@@ -44,12 +31,9 @@ export function buildDocRef(
|
|
|
44
31
|
if (config.useUserSubcollection) {
|
|
45
32
|
return doc(db, "users", userId, config.collectionName, docId);
|
|
46
33
|
}
|
|
47
|
-
return doc(db, config.collectionName,
|
|
34
|
+
return doc(db, config.collectionName, userId);
|
|
48
35
|
}
|
|
49
36
|
|
|
50
|
-
/**
|
|
51
|
-
* Get Firestore instance or throw error
|
|
52
|
-
*/
|
|
53
37
|
export function requireFirestore(): Firestore {
|
|
54
38
|
const db = getFirestore();
|
|
55
39
|
if (!db) {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Result Utilities
|
|
3
|
-
* Shared helpers for working with Result pattern
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import type { Result } from "../../utils/Result";
|
|
7
2
|
import { failure } from "../../utils/Result";
|
|
8
3
|
|
|
@@ -11,9 +6,6 @@ interface ApiError {
|
|
|
11
6
|
code: string;
|
|
12
7
|
}
|
|
13
8
|
|
|
14
|
-
/**
|
|
15
|
-
* Create a standard error result
|
|
16
|
-
*/
|
|
17
9
|
function createErrorResult(
|
|
18
10
|
message: string,
|
|
19
11
|
code: string = "UNKNOWN_ERROR"
|
|
@@ -21,12 +13,8 @@ function createErrorResult(
|
|
|
21
13
|
return failure({ message, code });
|
|
22
14
|
}
|
|
23
15
|
|
|
24
|
-
/**
|
|
25
|
-
* Map Error to ApiError result
|
|
26
|
-
*/
|
|
27
16
|
export function mapErrorToResult<T>(error: unknown): Result<T, ApiError> {
|
|
28
17
|
const message = error instanceof Error ? error.message : "An unknown error occurred";
|
|
29
18
|
const code = error instanceof Error && "code" in error ? String(error.code) : "UNKNOWN_ERROR";
|
|
30
19
|
return createErrorResult(message, code);
|
|
31
20
|
}
|
|
32
|
-
|
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User Cache Cleanup Hook
|
|
3
|
-
* Automatically cleans up previous user's query cache when userId changes
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import { useEffect, useRef } from "react";
|
|
7
2
|
import type { QueryClient } from "@umituz/react-native-design-system";
|
|
8
3
|
import { isAuthenticated } from "../../../../domains/subscription/utils/authGuards";
|
|
9
4
|
|
|
10
|
-
/**
|
|
11
|
-
* Cleans up previous user's cache when userId changes (logout or user switch)
|
|
12
|
-
* Prevents data leakage between users
|
|
13
|
-
*
|
|
14
|
-
* @param userId - Current user ID
|
|
15
|
-
* @param queryClient - TanStack Query client
|
|
16
|
-
* @param queryKey - Query key factory function that takes userId
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* usePreviousUserCleanup(userId, queryClient, (id) => creditsQueryKeys.user(id));
|
|
20
|
-
*/
|
|
21
5
|
export function usePreviousUserCleanup(
|
|
22
6
|
userId: string | null | undefined,
|
|
23
7
|
queryClient: QueryClient,
|
|
@@ -29,7 +13,6 @@ export function usePreviousUserCleanup(
|
|
|
29
13
|
const prevUserId = prevUserIdRef.current;
|
|
30
14
|
prevUserIdRef.current = userId;
|
|
31
15
|
|
|
32
|
-
// Clear previous user's cache when userId changes (logout or user switch)
|
|
33
16
|
if (prevUserId !== userId && isAuthenticated(prevUserId)) {
|
|
34
17
|
queryClient.removeQueries({
|
|
35
18
|
queryKey: queryKey(prevUserId),
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared TanStack Query Configuration
|
|
3
|
-
* Common query configurations to ensure consistency across hooks
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configuration for queries that should never cache
|
|
8
|
-
* Used for real-time sensitive data (subscriptions, credits, transactions)
|
|
9
|
-
*
|
|
10
|
-
* - gcTime: 0 - Don't keep unused data in memory
|
|
11
|
-
* - staleTime: 0 - Always consider data stale
|
|
12
|
-
* - refetchOnMount: "always" - Always refetch when component mounts
|
|
13
|
-
* - refetchOnWindowFocus: "always" - Always refetch when window regains focus
|
|
14
|
-
* - refetchOnReconnect: "always" - Always refetch when reconnecting
|
|
15
|
-
*/
|
|
16
1
|
export const NO_CACHE_QUERY_CONFIG = {
|
|
17
2
|
gcTime: 0,
|
|
18
3
|
staleTime: 0,
|