flowboard-react 0.1.0

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 (90) hide show
  1. package/FlowboardReact.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +122 -0
  4. package/android/build.gradle +67 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/com/flowboardreact/FlowboardReactModule.kt +15 -0
  7. package/android/src/main/java/com/flowboardreact/FlowboardReactPackage.kt +33 -0
  8. package/ios/FlowboardReact.h +5 -0
  9. package/ios/FlowboardReact.mm +21 -0
  10. package/lib/module/Flowboard.js +167 -0
  11. package/lib/module/Flowboard.js.map +1 -0
  12. package/lib/module/FlowboardProvider.js +52 -0
  13. package/lib/module/FlowboardProvider.js.map +1 -0
  14. package/lib/module/NativeFlowboardReact.js +5 -0
  15. package/lib/module/NativeFlowboardReact.js.map +1 -0
  16. package/lib/module/components/FlowboardFlow.js +389 -0
  17. package/lib/module/components/FlowboardFlow.js.map +1 -0
  18. package/lib/module/components/FlowboardRenderer.js +1684 -0
  19. package/lib/module/components/FlowboardRenderer.js.map +1 -0
  20. package/lib/module/components/widgets/sliderRegistry.js +48 -0
  21. package/lib/module/components/widgets/sliderRegistry.js.map +1 -0
  22. package/lib/module/core/analyticsManager.js +110 -0
  23. package/lib/module/core/analyticsManager.js.map +1 -0
  24. package/lib/module/core/assetPreloader.js +72 -0
  25. package/lib/module/core/assetPreloader.js.map +1 -0
  26. package/lib/module/core/clientContext.js +105 -0
  27. package/lib/module/core/clientContext.js.map +1 -0
  28. package/lib/module/core/fontAwesome.js +110 -0
  29. package/lib/module/core/fontAwesome.js.map +1 -0
  30. package/lib/module/core/onboardingRepository.js +62 -0
  31. package/lib/module/core/onboardingRepository.js.map +1 -0
  32. package/lib/module/core/resolverService.js +58 -0
  33. package/lib/module/core/resolverService.js.map +1 -0
  34. package/lib/module/index.js +5 -0
  35. package/lib/module/index.js.map +1 -0
  36. package/lib/module/package.json +1 -0
  37. package/lib/module/types/flowboard.js +4 -0
  38. package/lib/module/types/flowboard.js.map +1 -0
  39. package/lib/module/types/react-native-vector-icons.d.js +2 -0
  40. package/lib/module/types/react-native-vector-icons.d.js.map +1 -0
  41. package/lib/module/utils/flowboardUtils.js +379 -0
  42. package/lib/module/utils/flowboardUtils.js.map +1 -0
  43. package/lib/typescript/package.json +1 -0
  44. package/lib/typescript/src/Flowboard.d.ts +33 -0
  45. package/lib/typescript/src/Flowboard.d.ts.map +1 -0
  46. package/lib/typescript/src/FlowboardProvider.d.ts +5 -0
  47. package/lib/typescript/src/FlowboardProvider.d.ts.map +1 -0
  48. package/lib/typescript/src/NativeFlowboardReact.d.ts +7 -0
  49. package/lib/typescript/src/NativeFlowboardReact.d.ts.map +1 -0
  50. package/lib/typescript/src/components/FlowboardFlow.d.ts +14 -0
  51. package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -0
  52. package/lib/typescript/src/components/FlowboardRenderer.d.ts +31 -0
  53. package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -0
  54. package/lib/typescript/src/components/widgets/sliderRegistry.d.ts +16 -0
  55. package/lib/typescript/src/components/widgets/sliderRegistry.d.ts.map +1 -0
  56. package/lib/typescript/src/core/analyticsManager.d.ts +42 -0
  57. package/lib/typescript/src/core/analyticsManager.d.ts.map +1 -0
  58. package/lib/typescript/src/core/assetPreloader.d.ts +8 -0
  59. package/lib/typescript/src/core/assetPreloader.d.ts.map +1 -0
  60. package/lib/typescript/src/core/clientContext.d.ts +27 -0
  61. package/lib/typescript/src/core/clientContext.d.ts.map +1 -0
  62. package/lib/typescript/src/core/fontAwesome.d.ts +8 -0
  63. package/lib/typescript/src/core/fontAwesome.d.ts.map +1 -0
  64. package/lib/typescript/src/core/onboardingRepository.d.ts +15 -0
  65. package/lib/typescript/src/core/onboardingRepository.d.ts.map +1 -0
  66. package/lib/typescript/src/core/resolverService.d.ts +11 -0
  67. package/lib/typescript/src/core/resolverService.d.ts.map +1 -0
  68. package/lib/typescript/src/index.d.ts +4 -0
  69. package/lib/typescript/src/index.d.ts.map +1 -0
  70. package/lib/typescript/src/types/flowboard.d.ts +34 -0
  71. package/lib/typescript/src/types/flowboard.d.ts.map +1 -0
  72. package/lib/typescript/src/utils/flowboardUtils.d.ts +31 -0
  73. package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -0
  74. package/package.json +192 -0
  75. package/src/Flowboard.ts +223 -0
  76. package/src/FlowboardProvider.tsx +60 -0
  77. package/src/NativeFlowboardReact.ts +7 -0
  78. package/src/components/FlowboardFlow.tsx +513 -0
  79. package/src/components/FlowboardRenderer.tsx +1957 -0
  80. package/src/components/widgets/sliderRegistry.tsx +56 -0
  81. package/src/core/analyticsManager.ts +125 -0
  82. package/src/core/assetPreloader.ts +103 -0
  83. package/src/core/clientContext.ts +132 -0
  84. package/src/core/fontAwesome.ts +90 -0
  85. package/src/core/onboardingRepository.ts +79 -0
  86. package/src/core/resolverService.ts +69 -0
  87. package/src/index.tsx +11 -0
  88. package/src/types/flowboard.ts +50 -0
  89. package/src/types/react-native-vector-icons.d.ts +15 -0
  90. package/src/utils/flowboardUtils.ts +400 -0
@@ -0,0 +1,223 @@
1
+ import { Alert } from 'react-native';
2
+ import { ClientContext } from './core/clientContext';
3
+ import { ResolverService } from './core/resolverService';
4
+ import { OnboardingRepository } from './core/onboardingRepository';
5
+ import { AnalyticsManager } from './core/analyticsManager';
6
+ import type { FlowboardData, FlowboardLaunchOptions } from './types/flowboard';
7
+
8
+ export type LaunchPayload = {
9
+ data: FlowboardData;
10
+ options: FlowboardLaunchOptions;
11
+ initialStep: number;
12
+ initialFormData: Record<string, any>;
13
+ };
14
+
15
+ type LaunchListener = (payload: LaunchPayload) => void;
16
+
17
+ export class Flowboard {
18
+ private static appId: string | null = null;
19
+ private static bundleId: string | null = null;
20
+ private static debug = false;
21
+ private static repository = new OnboardingRepository();
22
+ private static service = new ResolverService();
23
+ private static initPromise: Promise<void> | null = null;
24
+ private static listeners = new Set<LaunchListener>();
25
+
26
+ static subscribe(listener: LaunchListener): () => void {
27
+ Flowboard.listeners.add(listener);
28
+ return () => Flowboard.listeners.delete(listener);
29
+ }
30
+
31
+ static async init(params: {
32
+ appId: string;
33
+ bundleId: string;
34
+ debug?: boolean;
35
+ enableAnalytics?: boolean;
36
+ }): Promise<void> {
37
+ Flowboard.appId = params.appId;
38
+ Flowboard.bundleId = params.bundleId;
39
+ Flowboard.debug = params.debug ?? false;
40
+
41
+ Flowboard.log(
42
+ `Initialized with appId: ${Flowboard.appId}, bundleId: ${Flowboard.bundleId}`
43
+ );
44
+
45
+ try {
46
+ const clientContext = await ClientContext.create();
47
+ AnalyticsManager.instance.configure({
48
+ enabled: params.enableAnalytics ?? true,
49
+ context: clientContext,
50
+ });
51
+ } catch (error) {
52
+ Flowboard.log(`Failed to configure analytics: ${String(error)}`);
53
+ }
54
+
55
+ Flowboard.initPromise = Flowboard.initialize();
56
+ }
57
+
58
+ static async reinit(): Promise<void> {
59
+ Flowboard.log('Re-initializing. Clearing cache...');
60
+ await Flowboard.clearLocalCaches();
61
+ Flowboard.initPromise = Flowboard.fetchAndSave();
62
+ await Flowboard.initPromise;
63
+ }
64
+
65
+ static async launchOnboarding(
66
+ options: FlowboardLaunchOptions = {}
67
+ ): Promise<void> {
68
+ Flowboard.log('Attempting to launch onboarding...');
69
+ if (!Flowboard.appId) {
70
+ throw new Error(
71
+ 'Flowboard.init() must be called before launchOnboarding'
72
+ );
73
+ }
74
+
75
+ try {
76
+ const context =
77
+ AnalyticsManager.instance.clientContextSnapshot ??
78
+ (await ClientContext.create());
79
+ AnalyticsManager.instance.configure({
80
+ enabled: options.enableAnalytics ?? true,
81
+ context,
82
+ });
83
+ } catch (error) {
84
+ Flowboard.log(
85
+ `Failed to configure analytics for launch: ${String(error)}`
86
+ );
87
+ }
88
+
89
+ let data = await Flowboard.repository.getOnboardingJson();
90
+
91
+ if (!data) {
92
+ Flowboard.log('Data not in cache. Waiting for initialization...');
93
+ if (Flowboard.initPromise) {
94
+ try {
95
+ await Flowboard.initPromise;
96
+ } catch (error) {
97
+ Flowboard.log(`Initialization failed during wait: ${String(error)}`);
98
+ }
99
+ }
100
+ data = await Flowboard.repository.getOnboardingJson();
101
+
102
+ if (!data) {
103
+ Flowboard.log('Data still null after wait. Retrying fetch...');
104
+ try {
105
+ await Flowboard.fetchAndSave();
106
+ data = await Flowboard.repository.getOnboardingJson();
107
+ } catch (error) {
108
+ Flowboard.log(`Retry fetch failed: ${String(error)}`);
109
+ Alert.alert('Failed to load onboarding', String(error));
110
+ return;
111
+ }
112
+ }
113
+ }
114
+
115
+ if (!data) {
116
+ Flowboard.log('Failed to launch: Data is null.');
117
+ return;
118
+ }
119
+
120
+ if (options.alwaysRestart) {
121
+ await Flowboard.repository.clearProgress();
122
+ }
123
+
124
+ let initialStep = 0;
125
+ let initialFormData: Record<string, any> = {};
126
+
127
+ if (!options.alwaysRestart) {
128
+ const flowId = data.flow_id as string | undefined;
129
+ if (flowId) {
130
+ const savedStep = await Flowboard.repository.getProgressStepForFlow(
131
+ flowId
132
+ );
133
+ const savedFormData =
134
+ await Flowboard.repository.getProgressFormDataForFlow(flowId);
135
+ if (savedStep !== null && savedStep !== undefined) {
136
+ const screens = Array.isArray(data.screens) ? data.screens : [];
137
+ if (screens.length > 0) {
138
+ const maxIndex = screens.length - 1;
139
+ if (savedStep <= 0) initialStep = 0;
140
+ else if (savedStep >= screens.length) initialStep = maxIndex;
141
+ else initialStep = savedStep;
142
+ }
143
+ }
144
+ if (savedFormData && Object.keys(savedFormData).length > 0) {
145
+ initialFormData = { ...savedFormData };
146
+ }
147
+ }
148
+ }
149
+
150
+ Flowboard.log(`Launching onboarding flow. ${data.flow_id ?? ''}`);
151
+ Flowboard.emitLaunch({
152
+ data,
153
+ options,
154
+ initialStep,
155
+ initialFormData,
156
+ });
157
+ }
158
+
159
+ private static async initialize(): Promise<void> {
160
+ Flowboard.log('Checking local cache...');
161
+ const cached = await Flowboard.repository.getOnboardingJson();
162
+ if (cached) {
163
+ Flowboard.log('Onboarding data found in cache.');
164
+ AnalyticsManager.instance.startSession({ flowData: cached });
165
+ return;
166
+ }
167
+
168
+ Flowboard.log('No cached data found. Fetching from remote...');
169
+ await Flowboard.fetchAndSave();
170
+ }
171
+
172
+ private static async fetchAndSave(): Promise<void> {
173
+ const startTime = Date.now();
174
+ if (!Flowboard.appId || !Flowboard.bundleId) {
175
+ throw new Error(
176
+ 'Flowboard not initialized. Call Flowboard.init() first.'
177
+ );
178
+ }
179
+
180
+ try {
181
+ const context = await ClientContext.create();
182
+ Flowboard.log(
183
+ `Fetching onboarding JSON for installId: ${context.installId} | appId: ${Flowboard.appId} | bundleId: ${Flowboard.bundleId}`
184
+ );
185
+
186
+ const json = await Flowboard.service.fetchOnboardingJson({
187
+ context,
188
+ appId: Flowboard.appId,
189
+ });
190
+
191
+ Flowboard.log('Fetch successful. Saving to cache.');
192
+ await Flowboard.repository.saveOnboardingJson(json);
193
+
194
+ AnalyticsManager.instance.startSession({ flowData: json });
195
+
196
+ const duration = Date.now() - startTime;
197
+ AnalyticsManager.instance.trackOnboardLoaded({
198
+ durationMs: duration,
199
+ cached: false,
200
+ });
201
+ } catch (error) {
202
+ Flowboard.log(`Error fetching onboarding data: ${String(error)}`);
203
+ throw error;
204
+ }
205
+ }
206
+
207
+ private static async clearLocalCaches(): Promise<void> {
208
+ await Promise.all([
209
+ Flowboard.repository.clearProgress(),
210
+ Flowboard.repository.clearOnboardingJson(),
211
+ ]);
212
+ }
213
+
214
+ private static emitLaunch(payload: LaunchPayload): void {
215
+ Flowboard.listeners.forEach((listener) => listener(payload));
216
+ }
217
+
218
+ private static log(message: string): void {
219
+ if (Flowboard.debug) {
220
+ console.log(`\u{1F30A} Flowboard - ${message}`);
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,60 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Modal, StyleSheet, View } from 'react-native';
3
+ import { Flowboard, type LaunchPayload } from './Flowboard';
4
+ import FlowboardFlow from './components/FlowboardFlow';
5
+
6
+ const styles = StyleSheet.create({
7
+ modalContainer: {
8
+ flex: 1,
9
+ backgroundColor: 'transparent',
10
+ },
11
+ });
12
+
13
+ export default function FlowboardProvider({
14
+ children,
15
+ }: {
16
+ children: React.ReactNode;
17
+ }) {
18
+ const [payload, setPayload] = useState<LaunchPayload | null>(null);
19
+ const [visible, setVisible] = useState(false);
20
+
21
+ useEffect(() => {
22
+ const unsubscribe = Flowboard.subscribe((nextPayload) => {
23
+ setPayload(nextPayload);
24
+ setVisible(true);
25
+ });
26
+ return unsubscribe;
27
+ }, []);
28
+
29
+ const handleClose = () => {
30
+ setVisible(false);
31
+ setPayload(null);
32
+ };
33
+
34
+ return (
35
+ <>
36
+ {children}
37
+ <Modal
38
+ visible={visible}
39
+ animationType="slide"
40
+ transparent
41
+ onRequestClose={handleClose}
42
+ >
43
+ <View style={styles.modalContainer}>
44
+ {payload ? (
45
+ <FlowboardFlow
46
+ data={payload.data}
47
+ customScreenBuilder={payload.options.customScreenBuilder}
48
+ customActionBuilder={payload.options.customActionBuilder}
49
+ onOnboardEnd={payload.options.onOnboardEnd}
50
+ onStepChange={payload.options.onStepChange}
51
+ initialStep={payload.initialStep}
52
+ initialFormData={payload.initialFormData}
53
+ onClose={handleClose}
54
+ />
55
+ ) : null}
56
+ </View>
57
+ </Modal>
58
+ </>
59
+ );
60
+ }
@@ -0,0 +1,7 @@
1
+ import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
+
3
+ export interface Spec extends TurboModule {
4
+ multiply(a: number, b: number): number;
5
+ }
6
+
7
+ export default TurboModuleRegistry.getEnforcing<Spec>('FlowboardReact');