@umituz/react-native-subscription 3.1.10 → 3.1.12
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/presentation/useCreditsRealTime.ts +10 -5
- package/src/domains/credits/utils/creditValidation.ts +5 -26
- package/src/domains/paywall/hooks/usePaywallActions.ts +21 -133
- package/src/domains/paywall/hooks/usePaywallActions.types.ts +16 -0
- package/src/domains/paywall/hooks/usePaywallPurchase.ts +78 -0
- package/src/domains/paywall/hooks/usePaywallRestore.ts +66 -0
- package/src/domains/revenuecat/infrastructure/services/userSwitchCore.ts +116 -0
- package/src/domains/revenuecat/infrastructure/services/userSwitchHandler.ts +19 -237
- package/src/domains/revenuecat/infrastructure/services/userSwitchHelpers.ts +55 -0
- package/src/domains/revenuecat/infrastructure/services/userSwitchInitializer.ts +143 -0
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +6 -3
- package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +2 -2
- package/src/domains/subscription/infrastructure/managers/packageHandlerFactory.ts +2 -2
- package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +2 -2
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.logic.ts +52 -0
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.tsx +15 -89
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.types.ts +59 -0
- package/src/domains/subscription/presentation/components/details/CreditRow.tsx +9 -0
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.tsx +23 -0
- package/src/domains/subscription/presentation/components/states/FeedbackState.tsx +36 -0
- package/src/domains/subscription/presentation/components/states/InitializingState.tsx +47 -0
- package/src/domains/subscription/presentation/components/states/OnboardingState.tsx +27 -0
- package/src/domains/subscription/presentation/components/states/PaywallState.tsx +66 -0
- package/src/domains/subscription/presentation/components/states/ReadyState.tsx +51 -0
- package/src/domains/subscription/presentation/providers/SubscriptionFlowProvider.tsx +8 -2
- package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx +119 -103
- package/src/domains/wallet/presentation/components/BalanceCard.tsx +7 -0
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +11 -0
- package/src/index.components.ts +1 -1
- package/src/shared/infrastructure/SubscriptionEventBus.ts +4 -2
- package/src/shared/presentation/hooks/useFirestoreRealTime.ts +22 -6
- package/src/shared/utils/errors/errorAssertions.ts +35 -0
- package/src/shared/utils/errors/errorConversion.ts +73 -0
- package/src/shared/utils/errors/errorTypeGuards.ts +27 -0
- package/src/shared/utils/errors/errorWrappers.ts +54 -0
- package/src/shared/utils/errors/index.ts +19 -0
- package/src/shared/utils/errors/serviceErrors.ts +36 -0
- package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.states.tsx +0 -187
- package/src/shared/utils/errorUtils.ts +0 -195
|
@@ -2,77 +2,21 @@
|
|
|
2
2
|
* ManagedSubscriptionFlow
|
|
3
3
|
*
|
|
4
4
|
* Clean state machine-based flow orchestration.
|
|
5
|
-
*
|
|
5
|
+
* State components and logic separated to individual modules.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React
|
|
9
|
-
import type { NavigationProp } from "@react-navigation/native";
|
|
10
|
-
import type { ImageSourcePropType } from "react-native";
|
|
8
|
+
import React from "react";
|
|
11
9
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
12
10
|
import { usePremiumStatus } from "../../presentation/usePremiumStatus";
|
|
13
11
|
import { usePremiumPackages } from "../../presentation/usePremiumPackages";
|
|
14
12
|
import { usePremiumActions } from "../../presentation/usePremiumActions";
|
|
15
13
|
import { useSubscriptionFlowStore, SubscriptionFlowStatus } from "../useSubscriptionFlow";
|
|
16
|
-
import
|
|
17
|
-
import type {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
ReadyState,
|
|
23
|
-
} from "./ManagedSubscriptionFlow.states";
|
|
24
|
-
|
|
25
|
-
export interface ManagedSubscriptionFlowProps {
|
|
26
|
-
children: React.ReactNode;
|
|
27
|
-
navigation: NavigationProp<any>;
|
|
28
|
-
islocalizationReady: boolean;
|
|
29
|
-
|
|
30
|
-
// Splash Configuration
|
|
31
|
-
splash?: {
|
|
32
|
-
appName: string;
|
|
33
|
-
tagline: string;
|
|
34
|
-
duration?: number;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Onboarding Configuration
|
|
38
|
-
onboarding: {
|
|
39
|
-
slides: any[];
|
|
40
|
-
translations: {
|
|
41
|
-
nextButton: string;
|
|
42
|
-
getStartedButton: string;
|
|
43
|
-
of: string;
|
|
44
|
-
};
|
|
45
|
-
themeColors: any;
|
|
46
|
-
showSkipButton?: boolean;
|
|
47
|
-
showBackButton?: boolean;
|
|
48
|
-
showProgressBar?: boolean;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
// Paywall Configuration
|
|
52
|
-
paywall: {
|
|
53
|
-
translations: PaywallTranslations;
|
|
54
|
-
features: SubscriptionFeature[];
|
|
55
|
-
legalUrls: PaywallLegalUrls;
|
|
56
|
-
heroImage: ImageSourcePropType;
|
|
57
|
-
bestValueIdentifier?: string;
|
|
58
|
-
creditsLabel?: string;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// Feedback Configuration
|
|
62
|
-
feedback: {
|
|
63
|
-
translations: PaywallFeedbackTranslations;
|
|
64
|
-
onSubmit?: (data: { reason: string; otherText?: string }) => void | Promise<void>;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// Offline Configuration (optional)
|
|
68
|
-
offline?: {
|
|
69
|
-
isOffline: boolean;
|
|
70
|
-
message: string;
|
|
71
|
-
backgroundColor?: string;
|
|
72
|
-
position?: "top" | "bottom";
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
14
|
+
import { useStateTransitions } from "./ManagedSubscriptionFlow.logic";
|
|
15
|
+
import type { ManagedSubscriptionFlowProps } from "./ManagedSubscriptionFlow.types";
|
|
16
|
+
import { InitializingState } from "./states/InitializingState";
|
|
17
|
+
import { OnboardingState } from "./states/OnboardingState";
|
|
18
|
+
import { PaywallState } from "./states/PaywallState";
|
|
19
|
+
import { ReadyState } from "./states/ReadyState";
|
|
76
20
|
import {
|
|
77
21
|
SubscriptionFlowProvider,
|
|
78
22
|
useSubscriptionFlowStatus
|
|
@@ -97,35 +41,17 @@ const ManagedSubscriptionFlowInner: React.FC<ManagedSubscriptionFlowProps> = ({
|
|
|
97
41
|
|
|
98
42
|
// Store actions
|
|
99
43
|
const completeOnboarding = useSubscriptionFlowStore((s) => s.completeOnboarding);
|
|
100
|
-
const showPaywall = useSubscriptionFlowStore((s) => s.showPaywall);
|
|
101
44
|
const completePaywall = useSubscriptionFlowStore((s) => s.completePaywall);
|
|
102
45
|
const hideFeedback = useSubscriptionFlowStore((s) => s.hideFeedback);
|
|
103
|
-
const showFeedbackScreen = useSubscriptionFlowStore((s) => s.showFeedbackScreen);
|
|
104
46
|
const showFeedback = useSubscriptionFlowStore((s) => s.showFeedback);
|
|
105
47
|
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (isPremium) {
|
|
115
|
-
completePaywall(true);
|
|
116
|
-
} else if (!paywallShown) {
|
|
117
|
-
showPaywall();
|
|
118
|
-
} else {
|
|
119
|
-
completePaywall(false);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}, [status, isPremium, isSyncing, showPaywall, completePaywall]);
|
|
123
|
-
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (status === SubscriptionFlowStatus.READY && showFeedback) {
|
|
126
|
-
showFeedbackScreen();
|
|
127
|
-
}
|
|
128
|
-
}, [status, showFeedback, showFeedbackScreen]);
|
|
48
|
+
// State transitions
|
|
49
|
+
useStateTransitions({
|
|
50
|
+
status,
|
|
51
|
+
isPremium,
|
|
52
|
+
isSyncing,
|
|
53
|
+
showFeedback,
|
|
54
|
+
});
|
|
129
55
|
|
|
130
56
|
// ========================================================================
|
|
131
57
|
// RENDER BY STATE
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ManagedSubscriptionFlow Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { NavigationProp } from "@react-navigation/native";
|
|
6
|
+
import type { ImageSourcePropType } from "react-native";
|
|
7
|
+
import type { PaywallFeedbackTranslations } from "./feedback/PaywallFeedbackScreen.types";
|
|
8
|
+
import type { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../../../paywall/entities/types";
|
|
9
|
+
|
|
10
|
+
export interface ManagedSubscriptionFlowProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
navigation: NavigationProp<any>;
|
|
13
|
+
islocalizationReady: boolean;
|
|
14
|
+
|
|
15
|
+
// Splash Configuration
|
|
16
|
+
splash?: {
|
|
17
|
+
appName: string;
|
|
18
|
+
tagline: string;
|
|
19
|
+
duration?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Onboarding Configuration
|
|
23
|
+
onboarding: {
|
|
24
|
+
slides: any[];
|
|
25
|
+
translations: {
|
|
26
|
+
nextButton: string;
|
|
27
|
+
getStartedButton: string;
|
|
28
|
+
of: string;
|
|
29
|
+
};
|
|
30
|
+
themeColors: any;
|
|
31
|
+
showSkipButton?: boolean;
|
|
32
|
+
showBackButton?: boolean;
|
|
33
|
+
showProgressBar?: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Paywall Configuration
|
|
37
|
+
paywall: {
|
|
38
|
+
translations: PaywallTranslations;
|
|
39
|
+
features: SubscriptionFeature[];
|
|
40
|
+
legalUrls: PaywallLegalUrls;
|
|
41
|
+
heroImage: ImageSourcePropType;
|
|
42
|
+
bestValueIdentifier?: string;
|
|
43
|
+
creditsLabel?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Feedback Configuration
|
|
47
|
+
feedback: {
|
|
48
|
+
translations: PaywallFeedbackTranslations;
|
|
49
|
+
onSubmit?: (data: { reason: string; otherText?: string }) => void | Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Offline Configuration (optional)
|
|
53
|
+
offline?: {
|
|
54
|
+
isOffline: boolean;
|
|
55
|
+
message: string;
|
|
56
|
+
backgroundColor?: string;
|
|
57
|
+
position?: "top" | "bottom";
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -55,6 +55,15 @@ export const CreditRow: React.FC<CreditRowProps> = React.memo(({
|
|
|
55
55
|
)}
|
|
56
56
|
</View>
|
|
57
57
|
);
|
|
58
|
+
}, (prevProps, nextProps) => {
|
|
59
|
+
// PERFORMANCE: Custom comparison to prevent unnecessary re-renders
|
|
60
|
+
// Only re-render if these values actually change
|
|
61
|
+
return (
|
|
62
|
+
prevProps.label === nextProps.label &&
|
|
63
|
+
prevProps.current === nextProps.current &&
|
|
64
|
+
prevProps.total === nextProps.total &&
|
|
65
|
+
prevProps.remainingLabel === nextProps.remainingLabel
|
|
66
|
+
);
|
|
58
67
|
});
|
|
59
68
|
|
|
60
69
|
const styles = StyleSheet.create({
|
|
@@ -68,4 +68,27 @@ export const PremiumDetailsCard: React.FC<PremiumDetailsCardProps> = React.memo(
|
|
|
68
68
|
/>
|
|
69
69
|
</View>
|
|
70
70
|
);
|
|
71
|
+
}, (prevProps, nextProps) => {
|
|
72
|
+
// PERFORMANCE: Custom comparison to prevent unnecessary re-renders
|
|
73
|
+
// Deep comparison for credits array since it's frequently updated
|
|
74
|
+
const creditsEqual = prevProps.credits === nextProps.credits ||
|
|
75
|
+
(prevProps.credits?.length === nextProps.credits?.length &&
|
|
76
|
+
prevProps.credits?.every((credit, i) =>
|
|
77
|
+
credit.id === nextProps.credits?.[i]?.id &&
|
|
78
|
+
credit.current === nextProps.credits?.[i]?.current &&
|
|
79
|
+
credit.total === nextProps.credits?.[i]?.total &&
|
|
80
|
+
credit.label === nextProps.credits?.[i]?.label
|
|
81
|
+
));
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
prevProps.statusType === nextProps.statusType &&
|
|
85
|
+
prevProps.isPremium === nextProps.isPremium &&
|
|
86
|
+
prevProps.expirationDate === nextProps.expirationDate &&
|
|
87
|
+
prevProps.purchaseDate === nextProps.purchaseDate &&
|
|
88
|
+
prevProps.daysRemaining === nextProps.daysRemaining &&
|
|
89
|
+
creditsEqual &&
|
|
90
|
+
prevProps.translations === nextProps.translations &&
|
|
91
|
+
prevProps.onManageSubscription === nextProps.onManageSubscription &&
|
|
92
|
+
prevProps.onUpgrade === nextProps.onUpgrade
|
|
93
|
+
);
|
|
71
94
|
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays feedback screen to collect user input.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { PaywallFeedbackScreen } from "../feedback/PaywallFeedbackScreen";
|
|
9
|
+
import { usePaywallFeedbackSubmit } from "../../../../../presentation/hooks/feedback/useFeedbackSubmit";
|
|
10
|
+
import type { ManagedSubscriptionFlowProps } from "../ManagedSubscriptionFlow.types";
|
|
11
|
+
|
|
12
|
+
interface FeedbackStateProps {
|
|
13
|
+
config: ManagedSubscriptionFlowProps["feedback"];
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const FeedbackState: React.FC<FeedbackStateProps> = ({ config, onClose }) => {
|
|
18
|
+
const { submit: internalSubmit } = usePaywallFeedbackSubmit();
|
|
19
|
+
|
|
20
|
+
const handleSubmit = async (data: { reason: string; otherText?: string }) => {
|
|
21
|
+
if (config.onSubmit) {
|
|
22
|
+
await config.onSubmit(data);
|
|
23
|
+
} else {
|
|
24
|
+
const description = data.otherText ? `${data.reason}: ${data.otherText}` : data.reason;
|
|
25
|
+
await internalSubmit(description);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<PaywallFeedbackScreen
|
|
31
|
+
onClose={onClose}
|
|
32
|
+
onSubmit={handleSubmit}
|
|
33
|
+
translations={config.translations}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initializing State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays splash screen during initialization.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { View, Text, StyleSheet } from "react-native";
|
|
9
|
+
import type { ManagedSubscriptionFlowProps } from "../ManagedSubscriptionFlow.types";
|
|
10
|
+
|
|
11
|
+
interface InitializingStateProps {
|
|
12
|
+
tokens: any;
|
|
13
|
+
splash?: ManagedSubscriptionFlowProps["splash"];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const InitializingState: React.FC<InitializingStateProps> = ({ tokens, splash }) => {
|
|
17
|
+
if (!splash) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={[styles.container, { backgroundColor: tokens.colors.background }]}>
|
|
23
|
+
<Text style={[styles.appName, { color: tokens.colors.text }]}>
|
|
24
|
+
{splash.appName}
|
|
25
|
+
</Text>
|
|
26
|
+
<Text style={[styles.tagline, { color: tokens.colors.textSecondary }]}>
|
|
27
|
+
{splash.tagline}
|
|
28
|
+
</Text>
|
|
29
|
+
</View>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const styles = StyleSheet.create({
|
|
34
|
+
container: {
|
|
35
|
+
flex: 1,
|
|
36
|
+
justifyContent: "center",
|
|
37
|
+
alignItems: "center",
|
|
38
|
+
},
|
|
39
|
+
appName: {
|
|
40
|
+
fontSize: 32,
|
|
41
|
+
fontWeight: "bold",
|
|
42
|
+
marginBottom: 8,
|
|
43
|
+
},
|
|
44
|
+
tagline: {
|
|
45
|
+
fontSize: 16,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays onboarding slides to the user.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { OnboardingScreen } from "@umituz/react-native-design-system/onboarding";
|
|
9
|
+
import type { ManagedSubscriptionFlowProps } from "../ManagedSubscriptionFlow.types";
|
|
10
|
+
|
|
11
|
+
interface OnboardingStateProps {
|
|
12
|
+
config: ManagedSubscriptionFlowProps["onboarding"];
|
|
13
|
+
onComplete: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const OnboardingState: React.FC<OnboardingStateProps> = ({ config, onComplete }) => {
|
|
17
|
+
return (
|
|
18
|
+
<OnboardingScreen
|
|
19
|
+
slides={config.slides}
|
|
20
|
+
translations={config.translations}
|
|
21
|
+
onComplete={onComplete}
|
|
22
|
+
showSkipButton={config.showSkipButton ?? true}
|
|
23
|
+
showBackButton={config.showBackButton ?? true}
|
|
24
|
+
showProgressBar={config.showProgressBar ?? true}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paywall State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays paywall screen for purchase/restore.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState } from "react";
|
|
8
|
+
import type { PurchasesPackage } from "react-native-purchases";
|
|
9
|
+
import type { UserCredits } from "../../../../credits/core/Credits";
|
|
10
|
+
import { PaywallScreen } from "../../../../paywall/components/PaywallScreen";
|
|
11
|
+
import type { ManagedSubscriptionFlowProps } from "../ManagedSubscriptionFlow.types";
|
|
12
|
+
|
|
13
|
+
interface PaywallStateProps {
|
|
14
|
+
config: ManagedSubscriptionFlowProps["paywall"];
|
|
15
|
+
packages: PurchasesPackage[];
|
|
16
|
+
isPremium: boolean;
|
|
17
|
+
credits: UserCredits | null;
|
|
18
|
+
isSyncing: boolean;
|
|
19
|
+
onPurchase: (pkg: PurchasesPackage) => Promise<boolean>;
|
|
20
|
+
onRestore: () => Promise<boolean>;
|
|
21
|
+
onClose: (purchased: boolean) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const PaywallState: React.FC<PaywallStateProps> = ({
|
|
25
|
+
config,
|
|
26
|
+
packages,
|
|
27
|
+
isPremium,
|
|
28
|
+
credits,
|
|
29
|
+
isSyncing,
|
|
30
|
+
onPurchase,
|
|
31
|
+
onRestore,
|
|
32
|
+
onClose,
|
|
33
|
+
}) => {
|
|
34
|
+
const [purchaseSuccessful, setPurchaseSuccessful] = useState(false);
|
|
35
|
+
|
|
36
|
+
const handlePurchase = async (pkg: PurchasesPackage) => {
|
|
37
|
+
const result = await onPurchase(pkg);
|
|
38
|
+
if (result) {
|
|
39
|
+
setPurchaseSuccessful(true);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleClose = () => {
|
|
45
|
+
onClose(purchaseSuccessful);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<PaywallScreen
|
|
50
|
+
translations={config.translations}
|
|
51
|
+
legalUrls={config.legalUrls}
|
|
52
|
+
features={config.features}
|
|
53
|
+
bestValueIdentifier={config.bestValueIdentifier}
|
|
54
|
+
creditsLabel={config.creditsLabel}
|
|
55
|
+
heroImage={config.heroImage}
|
|
56
|
+
source="onboarding"
|
|
57
|
+
packages={packages}
|
|
58
|
+
isPremium={isPremium}
|
|
59
|
+
credits={credits}
|
|
60
|
+
isSyncing={isSyncing}
|
|
61
|
+
onPurchase={handlePurchase}
|
|
62
|
+
onRestore={onRestore}
|
|
63
|
+
onClose={handleClose}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ready State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays app content with optional offline banner and feedback.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { FeedbackState } from "./FeedbackState";
|
|
9
|
+
import type { ManagedSubscriptionFlowProps } from "../ManagedSubscriptionFlow.types";
|
|
10
|
+
|
|
11
|
+
interface ReadyStateProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
offline?: ManagedSubscriptionFlowProps["offline"];
|
|
14
|
+
feedbackConfig: ManagedSubscriptionFlowProps["feedback"];
|
|
15
|
+
showFeedback: boolean;
|
|
16
|
+
tokens: any;
|
|
17
|
+
onFeedbackClose: () => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ReadyState: React.FC<ReadyStateProps> = ({
|
|
21
|
+
children,
|
|
22
|
+
offline,
|
|
23
|
+
feedbackConfig,
|
|
24
|
+
showFeedback,
|
|
25
|
+
tokens,
|
|
26
|
+
onFeedbackClose,
|
|
27
|
+
}) => {
|
|
28
|
+
const { OfflineBanner } = require("@umituz/react-native-design-system/offline");
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
{children}
|
|
33
|
+
|
|
34
|
+
{offline && (
|
|
35
|
+
<OfflineBanner
|
|
36
|
+
visible={offline.isOffline}
|
|
37
|
+
message={offline.message}
|
|
38
|
+
backgroundColor={offline.backgroundColor || tokens.colors.error}
|
|
39
|
+
position={offline.position || "top"}
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
|
|
43
|
+
{showFeedback && (
|
|
44
|
+
<FeedbackState
|
|
45
|
+
config={feedbackConfig}
|
|
46
|
+
onClose={onFeedbackClose}
|
|
47
|
+
/>
|
|
48
|
+
)}
|
|
49
|
+
</>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -58,9 +58,15 @@ export const SubscriptionFlowProvider: React.FC<{ children: React.ReactNode }> =
|
|
|
58
58
|
nextStatus = SubscriptionFlowStatus.INITIALIZING;
|
|
59
59
|
} else if (!isOnboardingComplete) {
|
|
60
60
|
nextStatus = SubscriptionFlowStatus.ONBOARDING;
|
|
61
|
-
} else
|
|
62
|
-
|
|
61
|
+
} else if (
|
|
62
|
+
status === SubscriptionFlowStatus.ONBOARDING ||
|
|
63
|
+
status === SubscriptionFlowStatus.INITIALIZING
|
|
64
|
+
) {
|
|
65
|
+
// First time completing onboarding - transition to CHECK_PREMIUM once
|
|
63
66
|
nextStatus = SubscriptionFlowStatus.CHECK_PREMIUM;
|
|
67
|
+
} else {
|
|
68
|
+
// Already past onboarding - let ManagedSubscriptionFlow control the flow
|
|
69
|
+
nextStatus = status;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
if (nextStatus !== status) {
|