@umituz/react-native-subscription 3.1.9 → 3.1.11

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.
Files changed (45) hide show
  1. package/package.json +1 -1
  2. package/src/domains/credits/presentation/useCreditsRealTime.ts +31 -73
  3. package/src/domains/credits/utils/creditValidation.ts +5 -26
  4. package/src/domains/paywall/hooks/usePaywallActions.ts +21 -133
  5. package/src/domains/paywall/hooks/usePaywallActions.types.ts +16 -0
  6. package/src/domains/paywall/hooks/usePaywallPurchase.ts +78 -0
  7. package/src/domains/paywall/hooks/usePaywallRestore.ts +66 -0
  8. package/src/domains/revenuecat/infrastructure/services/userSwitchCore.ts +116 -0
  9. package/src/domains/revenuecat/infrastructure/services/userSwitchHandler.ts +19 -237
  10. package/src/domains/revenuecat/infrastructure/services/userSwitchHelpers.ts +55 -0
  11. package/src/domains/revenuecat/infrastructure/services/userSwitchInitializer.ts +143 -0
  12. package/src/domains/subscription/infrastructure/managers/SubscriptionManager.ts +6 -3
  13. package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +2 -2
  14. package/src/domains/subscription/infrastructure/managers/packageHandlerFactory.ts +2 -2
  15. package/src/domains/subscription/infrastructure/managers/subscriptionManagerUtils.ts +2 -2
  16. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.logic.ts +52 -0
  17. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.tsx +15 -89
  18. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.types.ts +59 -0
  19. package/src/domains/subscription/presentation/components/details/CreditRow.tsx +9 -0
  20. package/src/domains/subscription/presentation/components/details/PremiumDetailsCard.tsx +23 -0
  21. package/src/domains/subscription/presentation/components/states/FeedbackState.tsx +36 -0
  22. package/src/domains/subscription/presentation/components/states/InitializingState.tsx +47 -0
  23. package/src/domains/subscription/presentation/components/states/OnboardingState.tsx +27 -0
  24. package/src/domains/subscription/presentation/components/states/PaywallState.tsx +66 -0
  25. package/src/domains/subscription/presentation/components/states/ReadyState.tsx +51 -0
  26. package/src/domains/subscription/presentation/flowInitialState.ts +22 -0
  27. package/src/domains/subscription/presentation/flowTypes.ts +106 -0
  28. package/src/domains/subscription/presentation/screens/components/SubscriptionHeaderContent.tsx +119 -103
  29. package/src/domains/subscription/presentation/usePremiumActions.ts +5 -6
  30. package/src/domains/subscription/presentation/useSubscriptionFlow.ts +25 -92
  31. package/src/domains/wallet/presentation/components/BalanceCard.tsx +7 -0
  32. package/src/domains/wallet/presentation/components/TransactionItem.tsx +11 -0
  33. package/src/domains/wallet/presentation/hooks/useTransactionHistory.ts +34 -60
  34. package/src/index.components.ts +1 -1
  35. package/src/shared/infrastructure/SubscriptionEventBus.ts +4 -2
  36. package/src/shared/presentation/hooks/useFirestoreRealTime.ts +230 -0
  37. package/src/shared/presentation/types/hookState.types.ts +97 -0
  38. package/src/shared/utils/errors/errorAssertions.ts +35 -0
  39. package/src/shared/utils/errors/errorConversion.ts +73 -0
  40. package/src/shared/utils/errors/errorTypeGuards.ts +27 -0
  41. package/src/shared/utils/errors/errorWrappers.ts +54 -0
  42. package/src/shared/utils/errors/index.ts +19 -0
  43. package/src/shared/utils/errors/serviceErrors.ts +36 -0
  44. package/src/shared/utils/logger.ts +140 -0
  45. package/src/domains/subscription/presentation/components/ManagedSubscriptionFlow.states.tsx +0 -187
@@ -2,77 +2,21 @@
2
2
  * ManagedSubscriptionFlow
3
3
  *
4
4
  * Clean state machine-based flow orchestration.
5
- * All state components separated to individual files.
5
+ * State components and logic separated to individual modules.
6
6
  */
7
7
 
8
- import React, { useEffect } from "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 type { PaywallFeedbackTranslations } from "./feedback/PaywallFeedbackScreen.types";
17
- import type { PaywallTranslations, PaywallLegalUrls, SubscriptionFeature } from "../../../paywall/entities/types";
18
- import {
19
- InitializingState,
20
- OnboardingState,
21
- PaywallState,
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
- // STATE TRANSITIONS
108
- // ========================================================================
109
-
110
- useEffect(() => {
111
- if (status === SubscriptionFlowStatus.CHECK_PREMIUM && !isSyncing) {
112
- const paywallShown = useSubscriptionFlowStore.getState().paywallShown;
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
+ };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Subscription Flow Initial State
3
+ *
4
+ * Default state for the subscription flow state machine.
5
+ */
6
+
7
+ import { SubscriptionFlowStatus, SyncStatus, type SubscriptionFlowState } from "./flowTypes";
8
+
9
+ /**
10
+ * Initial state for the subscription flow store.
11
+ * Represents a fresh install scenario.
12
+ */
13
+ export const initialFlowState: SubscriptionFlowState = {
14
+ status: SubscriptionFlowStatus.INITIALIZING,
15
+ syncStatus: SyncStatus.IDLE,
16
+ syncError: null,
17
+ isOnboardingComplete: false,
18
+ paywallShown: false,
19
+ showFeedback: false,
20
+ isAuthModalOpen: false,
21
+ isInitialized: false,
22
+ };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Subscription Flow State Machine Types
3
+ *
4
+ * Type definitions for the subscription flow state machine.
5
+ * Separated from implementation for better maintainability.
6
+ */
7
+
8
+ /**
9
+ * States in the subscription flow.
10
+ * Represents the user's journey from first launch to premium access.
11
+ */
12
+ export enum SubscriptionFlowStatus {
13
+ /** App is initializing, determining next state */
14
+ INITIALIZING = "INITIALIZING",
15
+ /** User is seeing onboarding for the first time */
16
+ ONBOARDING = "ONBOARDING",
17
+ /** Checking if user has premium access */
18
+ CHECK_PREMIUM = "CHECK_PREMIUM",
19
+ /** Showing paywall after onboarding */
20
+ POST_ONBOARDING_PAYWALL = "POST_ONBOARDING_PAYWALL",
21
+ /** App is ready to use */
22
+ READY = "READY",
23
+ }
24
+
25
+ /**
26
+ * Sync status for subscription/credits data.
27
+ */
28
+ export enum SyncStatus {
29
+ /** No sync in progress */
30
+ IDLE = "IDLE",
31
+ /** Syncing data from RevenueCat/Firestore */
32
+ SYNCING = "SYNCING",
33
+ /** Sync completed successfully */
34
+ SUCCESS = "SUCCESS",
35
+ /** Sync failed with error */
36
+ ERROR = "ERROR",
37
+ }
38
+
39
+ /**
40
+ * Complete state shape for the subscription flow store.
41
+ */
42
+ export interface SubscriptionFlowState {
43
+ /** Current flow status */
44
+ status: SubscriptionFlowStatus;
45
+
46
+ /** Sync status for subscription/credits data */
47
+ syncStatus: SyncStatus;
48
+
49
+ /** Error message from last sync failure */
50
+ syncError: string | null;
51
+
52
+ /** Whether user has completed onboarding */
53
+ isOnboardingComplete: boolean;
54
+
55
+ /** Whether paywall has been shown at least once */
56
+ paywallShown: boolean;
57
+
58
+ /** Whether to show feedback screen */
59
+ showFeedback: boolean;
60
+
61
+ /** Whether auth modal is currently open */
62
+ isAuthModalOpen: boolean;
63
+
64
+ /** Whether store has been initialized */
65
+ isInitialized: boolean;
66
+ }
67
+
68
+ /**
69
+ * Actions available on the subscription flow store.
70
+ */
71
+ export interface SubscriptionFlowActions {
72
+ /** Mark onboarding as complete and transition to CHECK_PREMIUM */
73
+ completeOnboarding: () => void;
74
+
75
+ /** Show paywall and transition to POST_ONBOARDING_PAYWALL */
76
+ showPaywall: () => void;
77
+
78
+ /** Complete paywall interaction and transition to READY */
79
+ completePaywall: (purchased: boolean) => void;
80
+
81
+ /** Show feedback screen */
82
+ showFeedbackScreen: () => void;
83
+
84
+ /** Hide feedback screen */
85
+ hideFeedback: () => void;
86
+
87
+ /** Open/close auth modal */
88
+ setAuthModalOpen: (open: boolean) => void;
89
+
90
+ /** Update sync status */
91
+ setSyncStatus: (status: SyncStatus, error?: string | null) => void;
92
+
93
+ /** Set initialized flag (internal use) */
94
+ setInitialized: (initialized: boolean) => void;
95
+
96
+ /** Set flow status (internal use) */
97
+ setStatus: (status: SubscriptionFlowStatus) => void;
98
+
99
+ /** Reset flow to initial state */
100
+ resetFlow: () => void;
101
+ }
102
+
103
+ /**
104
+ * Combined store type with state and actions.
105
+ */
106
+ export type SubscriptionFlowStore = SubscriptionFlowState & SubscriptionFlowActions;