flowboard-react 0.6.10 → 0.6.13

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.
@@ -3,6 +3,7 @@
3
3
  import { Platform } from 'react-native';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
  import 'react-native-get-random-values';
6
+ import { getTrackUrl } from "./apiConfig.js";
6
7
  export let OnboardingOutcome = /*#__PURE__*/function (OnboardingOutcome) {
7
8
  OnboardingOutcome["completed"] = "completed";
8
9
  OnboardingOutcome["dismissed"] = "dismissed";
@@ -10,7 +11,6 @@ export let OnboardingOutcome = /*#__PURE__*/function (OnboardingOutcome) {
10
11
  OnboardingOutcome["failed"] = "failed";
11
12
  return OnboardingOutcome;
12
13
  }({});
13
- const ANALYTICS_ENDPOINT = 'https://track-638704832888.europe-west1.run.app';
14
14
  export class AnalyticsManager {
15
15
  static _instance = new AnalyticsManager();
16
16
  static get instance() {
@@ -19,12 +19,14 @@ export class AnalyticsManager {
19
19
  enabled = true;
20
20
  clientContext = null;
21
21
  flowContext = {};
22
+ apiToken = null;
22
23
  get clientContextSnapshot() {
23
24
  return this.clientContext;
24
25
  }
25
26
  configure(params) {
26
27
  this.enabled = params.enabled ?? true;
27
28
  this.clientContext = params.context;
29
+ this.apiToken = params.apiToken.trim();
28
30
  }
29
31
  startSession(params) {
30
32
  this.flowContext = {
@@ -66,10 +68,14 @@ export class AnalyticsManager {
66
68
  }
67
69
  }
68
70
  async transmit(payload) {
71
+ if (!this.apiToken) {
72
+ return;
73
+ }
69
74
  try {
70
- const response = await fetch(ANALYTICS_ENDPOINT, {
75
+ const response = await fetch(getTrackUrl(), {
71
76
  method: 'POST',
72
77
  headers: {
78
+ 'Authorization': `Bearer ${this.apiToken}`,
73
79
  'Content-Type': 'application/json'
74
80
  },
75
81
  body: JSON.stringify(payload)
@@ -1 +1 @@
1
- {"version":3,"names":["Platform","v4","uuidv4","OnboardingOutcome","ANALYTICS_ENDPOINT","AnalyticsManager","_instance","instance","enabled","clientContext","flowContext","clientContextSnapshot","configure","params","context","startSession","flowData","screens","sessionId","session_id","endSession","track","eventName","properties","eventId","timestamp","Date","toISOString","payload","event_id","name","bundle_id","bundleId","flow_id","user_id","device_id","installId","platform","os","OS","app_version","appVersion","sdk_version","locale","flow_context","transmit","response","fetch","method","headers","body","JSON","stringify","ok","trackOnboardLoaded","duration_ms","durationMs","cached","trackOnboardStarted","trackScreenView","step_id","stepId","step_index","stepIndex","stepName","step_name","trackOnboardEnded","outcome","total_duration_ms","totalDurationMs","final_step_id","finalStepId"],"sourceRoot":"../../../src","sources":["core/analyticsManager.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["Platform","v4","uuidv4","getTrackUrl","OnboardingOutcome","AnalyticsManager","_instance","instance","enabled","clientContext","flowContext","apiToken","clientContextSnapshot","configure","params","context","trim","startSession","flowData","screens","sessionId","session_id","endSession","track","eventName","properties","eventId","timestamp","Date","toISOString","payload","event_id","name","bundle_id","bundleId","flow_id","user_id","device_id","installId","platform","os","OS","app_version","appVersion","sdk_version","locale","flow_context","transmit","response","fetch","method","headers","body","JSON","stringify","ok","trackOnboardLoaded","duration_ms","durationMs","cached","trackOnboardStarted","trackScreenView","step_id","stepId","step_index","stepIndex","stepName","step_name","trackOnboardEnded","outcome","total_duration_ms","totalDurationMs","final_step_id","finalStepId"],"sourceRoot":"../../../src","sources":["core/analyticsManager.ts"],"mappings":"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","ignoreList":[]}
@@ -37,6 +37,9 @@ export function getFlowboardRuntimeBaseUrl() {
37
37
  export function getResolveFlowUrl() {
38
38
  return `${getFlowboardRuntimeBaseUrl()}/flows`;
39
39
  }
40
+ export function getTrackUrl() {
41
+ return `${getFlowboardRuntimeBaseUrl()}/track`;
42
+ }
40
43
  export function getFlowByIdUrl(flowId) {
41
44
  const normalizedFlowId = String(flowId || '').trim();
42
45
  if (!normalizedFlowId) {
@@ -1 +1 @@
1
- {"version":3,"names":["DEFAULT_API_BASE_URL","DEFAULT_API_VERSION","trimTrailingSlashes","value","replace","trimSurroundingSlashes","normalizeApiBaseUrl","trimmed","String","trim","Error","test","normalizeApiVersion","getFlowboardApiBaseUrl","getFlowboardApiVersion","getFlowboardRuntimeBaseUrl","getResolveFlowUrl","getFlowByIdUrl","flowId","normalizedFlowId","encodeURIComponent"],"sourceRoot":"../../../src","sources":["core/apiConfig.ts"],"mappings":";;AAAA,MAAMA,oBAAoB,GAAG,mBAAmB;AAChD,MAAMC,mBAAmB,GAAG,IAAI;AAEhC,SAASC,mBAAmBA,CAACC,KAAa,EAAU;EAClD,OAAOA,KAAK,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;AAClC;AAEA,SAASC,sBAAsBA,CAACF,KAAa,EAAU;EACrD,OAAOA,KAAK,CAACC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;AACxC;AAEA,SAASE,mBAAmBA,CAACH,KAAa,EAAU;EAClD,MAAMI,OAAO,GAAGL,mBAAmB,CAACM,MAAM,CAACL,KAAK,IAAI,EAAE,CAAC,CAACM,IAAI,CAAC,CAAC,CAAC;EAC/D,IAAI,CAACF,OAAO,EAAE;IACZ,MAAM,IAAIG,KAAK,CAAC,oDAAoD,CAAC;EACvE;EAEA,IAAI,eAAe,CAACC,IAAI,CAACJ,OAAO,CAAC,EAAE;IACjC,OAAOA,OAAO;EAChB;EAEA,OAAO,WAAWA,OAAO,EAAE;AAC7B;AAEA,SAASK,mBAAmBA,CAACT,KAAa,EAAU;EAClD,MAAMI,OAAO,GAAGF,sBAAsB,CAACG,MAAM,CAACL,KAAK,IAAI,EAAE,CAAC,CAACM,IAAI,CAAC,CAAC,CAAC;EAClE,IAAI,CAACF,OAAO,EAAE;IACZ,MAAM,IAAIG,KAAK,CAAC,mDAAmD,CAAC;EACtE;EAEA,OAAO,IAAIH,OAAO,EAAE;AACtB;AAEA,OAAO,SAASM,sBAAsBA,CAAA,EAAW;EAC/C,OAAOP,mBAAmB,CAACN,oBAAoB,CAAC;AAClD;AAEA,OAAO,SAASc,sBAAsBA,CAAA,EAAW;EAC/C,OAAOF,mBAAmB,CAACX,mBAAmB,CAAC;AACjD;AAEA,OAAO,SAASc,0BAA0BA,CAAA,EAAW;EACnD,OAAO,GAAGF,sBAAsB,CAAC,CAAC,GAAGC,sBAAsB,CAAC,CAAC,EAAE;AACjE;AAEA,OAAO,SAASE,iBAAiBA,CAAA,EAAW;EAC1C,OAAO,GAAGD,0BAA0B,CAAC,CAAC,QAAQ;AAChD;AAEA,OAAO,SAASE,cAAcA,CAACC,MAAc,EAAU;EACrD,MAAMC,gBAAgB,GAAGX,MAAM,CAACU,MAAM,IAAI,EAAE,CAAC,CAACT,IAAI,CAAC,CAAC;EACpD,IAAI,CAACU,gBAAgB,EAAE;IACrB,MAAM,IAAIT,KAAK,CAAC,oCAAoC,CAAC;EACvD;EAEA,OAAO,GAAGK,0BAA0B,CAAC,CAAC,UAAUK,kBAAkB,CAChED,gBACF,CAAC,EAAE;AACL","ignoreList":[]}
1
+ {"version":3,"names":["DEFAULT_API_BASE_URL","DEFAULT_API_VERSION","trimTrailingSlashes","value","replace","trimSurroundingSlashes","normalizeApiBaseUrl","trimmed","String","trim","Error","test","normalizeApiVersion","getFlowboardApiBaseUrl","getFlowboardApiVersion","getFlowboardRuntimeBaseUrl","getResolveFlowUrl","getTrackUrl","getFlowByIdUrl","flowId","normalizedFlowId","encodeURIComponent"],"sourceRoot":"../../../src","sources":["core/apiConfig.ts"],"mappings":";;AAAA,MAAMA,oBAAoB,GAAG,mBAAmB;AAChD,MAAMC,mBAAmB,GAAG,IAAI;AAEhC,SAASC,mBAAmBA,CAACC,KAAa,EAAU;EAClD,OAAOA,KAAK,CAACC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;AAClC;AAEA,SAASC,sBAAsBA,CAACF,KAAa,EAAU;EACrD,OAAOA,KAAK,CAACC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;AACxC;AAEA,SAASE,mBAAmBA,CAACH,KAAa,EAAU;EAClD,MAAMI,OAAO,GAAGL,mBAAmB,CAACM,MAAM,CAACL,KAAK,IAAI,EAAE,CAAC,CAACM,IAAI,CAAC,CAAC,CAAC;EAC/D,IAAI,CAACF,OAAO,EAAE;IACZ,MAAM,IAAIG,KAAK,CAAC,oDAAoD,CAAC;EACvE;EAEA,IAAI,eAAe,CAACC,IAAI,CAACJ,OAAO,CAAC,EAAE;IACjC,OAAOA,OAAO;EAChB;EAEA,OAAO,WAAWA,OAAO,EAAE;AAC7B;AAEA,SAASK,mBAAmBA,CAACT,KAAa,EAAU;EAClD,MAAMI,OAAO,GAAGF,sBAAsB,CAACG,MAAM,CAACL,KAAK,IAAI,EAAE,CAAC,CAACM,IAAI,CAAC,CAAC,CAAC;EAClE,IAAI,CAACF,OAAO,EAAE;IACZ,MAAM,IAAIG,KAAK,CAAC,mDAAmD,CAAC;EACtE;EAEA,OAAO,IAAIH,OAAO,EAAE;AACtB;AAEA,OAAO,SAASM,sBAAsBA,CAAA,EAAW;EAC/C,OAAOP,mBAAmB,CAACN,oBAAoB,CAAC;AAClD;AAEA,OAAO,SAASc,sBAAsBA,CAAA,EAAW;EAC/C,OAAOF,mBAAmB,CAACX,mBAAmB,CAAC;AACjD;AAEA,OAAO,SAASc,0BAA0BA,CAAA,EAAW;EACnD,OAAO,GAAGF,sBAAsB,CAAC,CAAC,GAAGC,sBAAsB,CAAC,CAAC,EAAE;AACjE;AAEA,OAAO,SAASE,iBAAiBA,CAAA,EAAW;EAC1C,OAAO,GAAGD,0BAA0B,CAAC,CAAC,QAAQ;AAChD;AAEA,OAAO,SAASE,WAAWA,CAAA,EAAW;EACpC,OAAO,GAAGF,0BAA0B,CAAC,CAAC,QAAQ;AAChD;AAEA,OAAO,SAASG,cAAcA,CAACC,MAAc,EAAU;EACrD,MAAMC,gBAAgB,GAAGZ,MAAM,CAACW,MAAM,IAAI,EAAE,CAAC,CAACV,IAAI,CAAC,CAAC;EACpD,IAAI,CAACW,gBAAgB,EAAE;IACrB,MAAM,IAAIV,KAAK,CAAC,oCAAoC,CAAC;EACvD;EAEA,OAAO,GAAGK,0BAA0B,CAAC,CAAC,UAAUM,kBAAkB,CAChED,gBACF,CAAC,EAAE;AACL","ignoreList":[]}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ import { Platform, Vibration } from 'react-native';
4
+ import { hasNativeModule, warnOnce } from "./runtime.js";
5
+ function loadExpoHaptics() {
6
+ try {
7
+ return require('expo-haptics');
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+ function loadRNHapticFeedback() {
13
+ try {
14
+ return require('react-native-haptic-feedback');
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+ const expoHaptics = loadExpoHaptics();
20
+ const rnHapticFeedback = loadRNHapticFeedback();
21
+ const rnHapticsAvailable = hasNativeModule('RNHapticFeedback', 'RNReactNativeHapticFeedback') && typeof rnHapticFeedback?.trigger === 'function';
22
+ export function triggerSelectionHaptic() {
23
+ if (Platform.OS === 'web') return;
24
+ try {
25
+ if (typeof expoHaptics?.selectionAsync === 'function') {
26
+ void expoHaptics.selectionAsync();
27
+ return;
28
+ }
29
+ if (rnHapticsAvailable) {
30
+ rnHapticFeedback?.trigger?.('selection', {
31
+ enableVibrateFallback: false,
32
+ ignoreAndroidSystemSettings: false
33
+ });
34
+ return;
35
+ }
36
+ if (Platform.OS === 'android') {
37
+ Vibration.vibrate(4);
38
+ return;
39
+ }
40
+ warnOnce('native-haptics-missing-ios', 'No native haptics package is installed. Selection haptics are disabled on iOS to avoid heavy vibration.');
41
+ } catch {
42
+ warnOnce('native-haptics-error', 'Failed to trigger haptics. Flowboard will continue without tactile feedback.');
43
+ }
44
+ }
45
+ //# sourceMappingURL=haptics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Platform","Vibration","hasNativeModule","warnOnce","loadExpoHaptics","require","loadRNHapticFeedback","expoHaptics","rnHapticFeedback","rnHapticsAvailable","trigger","triggerSelectionHaptic","OS","selectionAsync","enableVibrateFallback","ignoreAndroidSystemSettings","vibrate"],"sourceRoot":"../../../src","sources":["native/haptics.ts"],"mappings":";;AAAA,SAASA,QAAQ,EAAEC,SAAS,QAAQ,cAAc;AAClD,SAASC,eAAe,EAAEC,QAAQ,QAAQ,cAAW;AAgBrD,SAASC,eAAeA,CAAA,EAA6B;EACnD,IAAI;IACF,OAAOC,OAAO,CAAC,cAAc,CAAC;EAChC,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF;AAEA,SAASC,oBAAoBA,CAAA,EAAkC;EAC7D,IAAI;IACF,OAAOD,OAAO,CAAC,8BAA8B,CAAC;EAChD,CAAC,CAAC,MAAM;IACN,OAAO,IAAI;EACb;AACF;AAEA,MAAME,WAAW,GAAGH,eAAe,CAAC,CAAC;AACrC,MAAMI,gBAAgB,GAAGF,oBAAoB,CAAC,CAAC;AAC/C,MAAMG,kBAAkB,GACtBP,eAAe,CAAC,kBAAkB,EAAE,6BAA6B,CAAC,IAClE,OAAOM,gBAAgB,EAAEE,OAAO,KAAK,UAAU;AAEjD,OAAO,SAASC,sBAAsBA,CAAA,EAAS;EAC7C,IAAIX,QAAQ,CAACY,EAAE,KAAK,KAAK,EAAE;EAE3B,IAAI;IACF,IAAI,OAAOL,WAAW,EAAEM,cAAc,KAAK,UAAU,EAAE;MACrD,KAAKN,WAAW,CAACM,cAAc,CAAC,CAAC;MACjC;IACF;IAEA,IAAIJ,kBAAkB,EAAE;MACtBD,gBAAgB,EAAEE,OAAO,GAAG,WAAW,EAAE;QACvCI,qBAAqB,EAAE,KAAK;QAC5BC,2BAA2B,EAAE;MAC/B,CAAC,CAAC;MACF;IACF;IAEA,IAAIf,QAAQ,CAACY,EAAE,KAAK,SAAS,EAAE;MAC7BX,SAAS,CAACe,OAAO,CAAC,CAAC,CAAC;MACpB;IACF;IAEAb,QAAQ,CACN,4BAA4B,EAC5B,yGACF,CAAC;EACH,CAAC,CAAC,MAAM;IACNA,QAAQ,CACN,sBAAsB,EACtB,8EACF,CAAC;EACH;AACF","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"Flowboard.d.ts","sourceRoot":"","sources":["../../../src/Flowboard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,sBAAsB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC,CAAC;AAEF,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;AAEvD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuB;IAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAC,UAAU,CAA8B;IACvD,OAAO,CAAC,MAAM,CAAC,OAAO,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8B;IACxD,OAAO,CAAC,MAAM,CAAC,SAAS,CAA6B;IAErD,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;WAKzC,IAAI,CAAC,MAAM,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;WAwBJ,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;WAOvB,gBAAgB,CAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC;WA4CH,oBAAoB,CAC/B,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC;mBAiCK,wBAAwB;mBA8CxB,2BAA2B;mBAkB3B,aAAa;mBAeb,UAAU;mBAaV,YAAY;mBAmCZ,gBAAgB;IAOrC,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB,OAAO,CAAC,MAAM,CAAC,GAAG;CAKnB"}
1
+ {"version":3,"file":"Flowboard.d.ts","sourceRoot":"","sources":["../../../src/Flowboard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,aAAa,EACb,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,sBAAsB,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACtC,CAAC;AAEF,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;AAEvD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuB;IAC9C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAC,UAAU,CAA8B;IACvD,OAAO,CAAC,MAAM,CAAC,OAAO,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8B;IACxD,OAAO,CAAC,MAAM,CAAC,SAAS,CAA6B;IAErD,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;WAKzC,IAAI,CAAC,MAAM,EAAE;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,eAAe,CAAC,EAAE,OAAO,CAAC;KAC3B,GAAG,OAAO,CAAC,IAAI,CAAC;WAyBJ,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;WAOvB,gBAAgB,CAC3B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,IAAI,CAAC;WA4CH,oBAAoB,CAC/B,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC;mBAiCK,wBAAwB;mBA8CxB,2BAA2B;mBAwB3B,aAAa;mBAeb,UAAU;mBAaV,YAAY;mBAmCZ,gBAAgB;IAOrC,OAAO,CAAC,MAAM,CAAC,UAAU;IAIzB,OAAO,CAAC,MAAM,CAAC,GAAG;CAKnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"FlowboardRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/FlowboardRenderer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FlowboardRenderer.d.ts","sourceRoot":"","sources":["../../../../src/components/FlowboardRenderer.tsx"],"names":[],"mappings":"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"}
@@ -12,10 +12,12 @@ export declare class AnalyticsManager {
12
12
  private enabled;
13
13
  private clientContext;
14
14
  private flowContext;
15
+ private apiToken;
15
16
  get clientContextSnapshot(): ClientContext | null;
16
17
  configure(params: {
17
18
  enabled?: boolean;
18
19
  context: ClientContext;
20
+ apiToken: string;
19
21
  }): void;
20
22
  startSession(params: {
21
23
  flowData: Record<string, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"analyticsManager.d.ts","sourceRoot":"","sources":["../../../../src/core/analyticsManager.ts"],"names":[],"mappings":"AAEA,OAAO,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,oBAAY,iBAAiB;IAC3B,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAID,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,SAAS,CAA0B;IAClD,MAAM,KAAK,QAAQ,IAAI,gBAAgB,CAEtC;IAED,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAA2B;IAE9C,IAAI,qBAAqB,IAAI,aAAa,GAAG,IAAI,CAEhD;IAED,SAAS,CAAC,MAAM,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI;IAKtE,YAAY,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,GAAG,IAAI;IAO7D,UAAU,IAAI,IAAI;IAIlB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;YA+BjD,QAAQ;IAetB,kBAAkB,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAOzE,mBAAmB,IAAI,IAAI;IAI3B,eAAe,CAAC,MAAM,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,GAAG,IAAI;IAQR,iBAAiB,CAAC,MAAM,EAAE;QACxB,OAAO,EAAE,iBAAiB,CAAC;QAC3B,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI;CAOT"}
1
+ {"version":3,"file":"analyticsManager.d.ts","sourceRoot":"","sources":["../../../../src/core/analyticsManager.ts"],"names":[],"mappings":"AAEA,OAAO,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,oBAAY,iBAAiB;IAC3B,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,MAAM,WAAW;CAClB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,SAAS,CAA0B;IAClD,MAAM,KAAK,QAAQ,IAAI,gBAAgB,CAEtC;IAED,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,QAAQ,CAAuB;IAEvC,IAAI,qBAAqB,IAAI,aAAa,GAAG,IAAI,CAEhD;IAED,SAAS,CAAC,MAAM,EAAE;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,aAAa,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,IAAI;IAMR,YAAY,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,GAAG,IAAI;IAO7D,UAAU,IAAI,IAAI;IAIlB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;YA+BjD,QAAQ;IAsBtB,kBAAkB,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAOzE,mBAAmB,IAAI,IAAI;IAI3B,eAAe,CAAC,MAAM,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,GAAG,IAAI;IAQR,iBAAiB,CAAC,MAAM,EAAE;QACxB,OAAO,EAAE,iBAAiB,CAAC;QAC3B,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;KACrB,GAAG,IAAI;CAOT"}
@@ -2,5 +2,6 @@ export declare function getFlowboardApiBaseUrl(): string;
2
2
  export declare function getFlowboardApiVersion(): string;
3
3
  export declare function getFlowboardRuntimeBaseUrl(): string;
4
4
  export declare function getResolveFlowUrl(): string;
5
+ export declare function getTrackUrl(): string;
5
6
  export declare function getFlowByIdUrl(flowId: string): string;
6
7
  //# sourceMappingURL=apiConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apiConfig.d.ts","sourceRoot":"","sources":["../../../../src/core/apiConfig.ts"],"names":[],"mappings":"AAiCA,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASrD"}
1
+ {"version":3,"file":"apiConfig.d.ts","sourceRoot":"","sources":["../../../../src/core/apiConfig.ts"],"names":[],"mappings":"AAiCA,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASrD"}
@@ -0,0 +1,2 @@
1
+ export declare function triggerSelectionHaptic(): void;
2
+ //# sourceMappingURL=haptics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haptics.d.ts","sourceRoot":"","sources":["../../../../src/native/haptics.ts"],"names":[],"mappings":"AAuCA,wBAAgB,sBAAsB,IAAI,IAAI,CAgC7C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowboard-react",
3
- "version": "0.6.10",
3
+ "version": "0.6.13",
4
4
  "description": "Onboard your users with one click",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -99,31 +99,39 @@
99
99
  "typescript": "^5.9.2"
100
100
  },
101
101
  "peerDependencies": {
102
- "@react-native-async-storage/async-storage": ">= 1.24.0",
103
- "@react-native-masked-view/masked-view": ">= 0.3.2",
104
- "expo-font": ">= 12.0.0",
105
- "lottie-react-native": ">= 7.1.0",
102
+ "@react-native-async-storage/async-storage": "*",
103
+ "@react-native-masked-view/masked-view": "*",
104
+ "expo-font": "*",
105
+ "expo-haptics": "*",
106
+ "lottie-react-native": "*",
106
107
  "react": ">= 18.2.0",
107
108
  "react-native": ">= 0.71.0",
108
- "react-native-device-info": ">= 13.2.0",
109
- "react-native-dynamic-fonts": ">= 1.0.0",
110
- "react-native-gesture-handler": ">= 2.30.0",
111
- "react-native-get-random-values": ">= 1.11.0",
112
- "react-native-in-app-review": ">= 4.3.0",
113
- "react-native-linear-gradient": ">= 2.8.3",
114
- "react-native-mask-input": ">= 1.2.3",
115
- "react-native-pager-view": ">= 6.2.4",
116
- "react-native-permissions": ">= 4.1.5",
117
- "react-native-safe-area-context": ">= 5.0.0",
118
- "react-native-svg": ">= 15.10.1",
119
- "react-native-vector-icons": ">= 10.2.0"
109
+ "react-native-device-info": "*",
110
+ "react-native-dynamic-fonts": "*",
111
+ "react-native-gesture-handler": "*",
112
+ "react-native-get-random-values": "*",
113
+ "react-native-haptic-feedback": "*",
114
+ "react-native-in-app-review": "*",
115
+ "react-native-linear-gradient": "*",
116
+ "react-native-mask-input": "*",
117
+ "react-native-pager-view": "*",
118
+ "react-native-permissions": "*",
119
+ "react-native-safe-area-context": "*",
120
+ "react-native-svg": "*",
121
+ "react-native-vector-icons": "*"
120
122
  },
121
123
  "peerDependenciesMeta": {
122
124
  "expo-font": {
123
125
  "optional": true
124
126
  },
127
+ "expo-haptics": {
128
+ "optional": true
129
+ },
125
130
  "react-native-dynamic-fonts": {
126
131
  "optional": true
132
+ },
133
+ "react-native-haptic-feedback": {
134
+ "optional": true
127
135
  }
128
136
  },
129
137
  "workspaces": [
package/src/Flowboard.ts CHANGED
@@ -51,6 +51,7 @@ export class Flowboard {
51
51
  AnalyticsManager.instance.configure({
52
52
  enabled: params.enableAnalytics ?? true,
53
53
  context: clientContext,
54
+ apiToken,
54
55
  });
55
56
  } catch (error) {
56
57
  Flowboard.log(`Failed to configure analytics: ${String(error)}`);
@@ -198,12 +199,18 @@ export class Flowboard {
198
199
  enableAnalytics: boolean | undefined
199
200
  ): Promise<void> {
200
201
  try {
202
+ if (!Flowboard.apiToken) {
203
+ throw new Error(
204
+ 'Flowboard.init() must be called before configuring analytics'
205
+ );
206
+ }
201
207
  const context =
202
208
  AnalyticsManager.instance.clientContextSnapshot ??
203
209
  (await ClientContext.create());
204
210
  AnalyticsManager.instance.configure({
205
211
  enabled: enableAnalytics ?? true,
206
212
  context,
213
+ apiToken: Flowboard.apiToken,
207
214
  });
208
215
  } catch (error) {
209
216
  Flowboard.log(
@@ -10,7 +10,6 @@ import {
10
10
  TextInput,
11
11
  View,
12
12
  Image,
13
- Vibration,
14
13
  type TextStyle,
15
14
  type ViewStyle,
16
15
  } from 'react-native';
@@ -24,6 +23,7 @@ import PagerView, {
24
23
  } from '../native/pagerView';
25
24
  import { useSafeAreaInsets } from '../native/safeAreaContext';
26
25
  import Svg, { Circle, G, Line, Polygon, SvgText } from '../native/svg';
26
+ import { triggerSelectionHaptic } from '../native/haptics';
27
27
  import {
28
28
  insetsToStyle,
29
29
  insetsToMarginStyle,
@@ -3299,10 +3299,6 @@ function WheelPicker({
3299
3299
  onAction: (action: string, data?: Record<string, any>) => void;
3300
3300
  buildWidget: (widgetJson: Record<string, any>) => React.ReactNode;
3301
3301
  }) {
3302
- const triggerLightHaptic = React.useCallback(() => {
3303
- Vibration.vibrate(8);
3304
- }, []);
3305
-
3306
3302
  const multiSelect = properties.multiSelect === true;
3307
3303
  const [selectedValues, setSelectedValues] = React.useState<string[]>(() => {
3308
3304
  if (initialValue === null || initialValue === undefined) return [];
@@ -3338,6 +3334,9 @@ function WheelPicker({
3338
3334
  const wheelScrollY = React.useRef(new Animated.Value(0)).current;
3339
3335
  const wheelScrollRef = React.useRef<any>(null);
3340
3336
  const lastCommittedWheelValue = React.useRef<string | null>(null);
3337
+ const lastWheelHapticIndex = React.useRef<number | null>(null);
3338
+ const lastWheelHapticAtMs = React.useRef(0);
3339
+ const isWheelInteractionActive = React.useRef(false);
3341
3340
 
3342
3341
  const toggleSelection = (value: string) => {
3343
3342
  setSelectedValues((prev) => {
@@ -3428,7 +3427,6 @@ function WheelPicker({
3428
3427
 
3429
3428
  if (lastCommittedWheelValue.current !== value) {
3430
3429
  lastCommittedWheelValue.current = value;
3431
- triggerLightHaptic();
3432
3430
  onChanged(value);
3433
3431
  if (properties.autoGoNext === true) {
3434
3432
  requestAnimationFrame(() =>
@@ -3443,13 +3441,13 @@ function WheelPicker({
3443
3441
  onChanged,
3444
3442
  properties.autoGoNext,
3445
3443
  resolvedOptions,
3446
- triggerLightHaptic,
3447
3444
  ]
3448
3445
  );
3449
3446
 
3450
3447
  React.useEffect(() => {
3451
3448
  if (layout !== 'wheel' || resolvedOptions.length === 0) return;
3452
3449
  const selectedIndex = getWheelSelectedIndex();
3450
+ lastWheelHapticIndex.current = selectedIndex;
3453
3451
  const offsetY = selectedIndex * wheelRowStride;
3454
3452
  requestAnimationFrame(() => {
3455
3453
  wheelScrollRef.current?.scrollTo({
@@ -3635,6 +3633,7 @@ function WheelPicker({
3635
3633
  const settledIndex = clampWheelIndex(
3636
3634
  Math.round(offsetY / wheelRowStride)
3637
3635
  );
3636
+ isWheelInteractionActive.current = false;
3638
3637
  const targetOffset = settledIndex * wheelRowStride;
3639
3638
  if (Math.abs(targetOffset - offsetY) > 0.5) {
3640
3639
  wheelScrollRef.current?.scrollTo({
@@ -3645,6 +3644,18 @@ function WheelPicker({
3645
3644
  commitWheelSelection(settledIndex);
3646
3645
  };
3647
3646
 
3647
+ const handleWheelScroll = (event: any) => {
3648
+ if (!isWheelInteractionActive.current) return;
3649
+ const offsetY = Number(event?.nativeEvent?.contentOffset?.y ?? 0);
3650
+ const index = clampWheelIndex(Math.round(offsetY / wheelRowStride));
3651
+ if (lastWheelHapticIndex.current === index) return;
3652
+ const now = Date.now();
3653
+ if (now - lastWheelHapticAtMs.current < 35) return;
3654
+ lastWheelHapticAtMs.current = now;
3655
+ lastWheelHapticIndex.current = index;
3656
+ triggerSelectionHaptic();
3657
+ };
3658
+
3648
3659
  return (
3649
3660
  <View
3650
3661
  style={{
@@ -3664,6 +3675,12 @@ function WheelPicker({
3664
3675
  snapToInterval={wheelRowStride}
3665
3676
  snapToAlignment="start"
3666
3677
  disableIntervalMomentum={false}
3678
+ onScrollBeginDrag={() => {
3679
+ isWheelInteractionActive.current = true;
3680
+ }}
3681
+ onMomentumScrollBegin={() => {
3682
+ isWheelInteractionActive.current = true;
3683
+ }}
3667
3684
  onMomentumScrollEnd={handleWheelMomentumEnd}
3668
3685
  contentContainerStyle={{
3669
3686
  width: '100%',
@@ -3672,7 +3689,7 @@ function WheelPicker({
3672
3689
  }}
3673
3690
  onScroll={Animated.event(
3674
3691
  [{ nativeEvent: { contentOffset: { y: wheelScrollY } } }],
3675
- { useNativeDriver: true }
3692
+ { useNativeDriver: true, listener: handleWheelScroll }
3676
3693
  )}
3677
3694
  scrollEventThrottle={16}
3678
3695
  >
@@ -2,6 +2,7 @@ import { Platform } from 'react-native';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
  import 'react-native-get-random-values';
4
4
  import type { ClientContext } from './clientContext';
5
+ import { getTrackUrl } from './apiConfig';
5
6
 
6
7
  export enum OnboardingOutcome {
7
8
  completed = 'completed',
@@ -10,8 +11,6 @@ export enum OnboardingOutcome {
10
11
  failed = 'failed',
11
12
  }
12
13
 
13
- const ANALYTICS_ENDPOINT = 'https://track-638704832888.europe-west1.run.app';
14
-
15
14
  export class AnalyticsManager {
16
15
  private static _instance = new AnalyticsManager();
17
16
  static get instance(): AnalyticsManager {
@@ -21,14 +20,20 @@ export class AnalyticsManager {
21
20
  private enabled = true;
22
21
  private clientContext: ClientContext | null = null;
23
22
  private flowContext: Record<string, any> = {};
23
+ private apiToken: string | null = null;
24
24
 
25
25
  get clientContextSnapshot(): ClientContext | null {
26
26
  return this.clientContext;
27
27
  }
28
28
 
29
- configure(params: { enabled?: boolean; context: ClientContext }): void {
29
+ configure(params: {
30
+ enabled?: boolean;
31
+ context: ClientContext;
32
+ apiToken: string;
33
+ }): void {
30
34
  this.enabled = params.enabled ?? true;
31
35
  this.clientContext = params.context;
36
+ this.apiToken = params.apiToken.trim();
32
37
  }
33
38
 
34
39
  startSession(params: { flowData: Record<string, any> }): void {
@@ -74,10 +79,17 @@ export class AnalyticsManager {
74
79
  }
75
80
 
76
81
  private async transmit(payload: Record<string, any>): Promise<void> {
82
+ if (!this.apiToken) {
83
+ return;
84
+ }
85
+
77
86
  try {
78
- const response = await fetch(ANALYTICS_ENDPOINT, {
87
+ const response = await fetch(getTrackUrl(), {
79
88
  method: 'POST',
80
- headers: { 'Content-Type': 'application/json' },
89
+ headers: {
90
+ 'Authorization': `Bearer ${this.apiToken}`,
91
+ 'Content-Type': 'application/json',
92
+ },
81
93
  body: JSON.stringify(payload),
82
94
  });
83
95
  if (!response.ok) {
@@ -47,6 +47,10 @@ export function getResolveFlowUrl(): string {
47
47
  return `${getFlowboardRuntimeBaseUrl()}/flows`;
48
48
  }
49
49
 
50
+ export function getTrackUrl(): string {
51
+ return `${getFlowboardRuntimeBaseUrl()}/track`;
52
+ }
53
+
50
54
  export function getFlowByIdUrl(flowId: string): string {
51
55
  const normalizedFlowId = String(flowId || '').trim();
52
56
  if (!normalizedFlowId) {
@@ -0,0 +1,72 @@
1
+ import { Platform, Vibration } from 'react-native';
2
+ import { hasNativeModule, warnOnce } from './runtime';
3
+
4
+ type ExpoHapticsModule = {
5
+ selectionAsync?: () => Promise<void>;
6
+ };
7
+
8
+ type RNHapticFeedbackModule = {
9
+ trigger?: (
10
+ method: string,
11
+ options?: {
12
+ enableVibrateFallback?: boolean;
13
+ ignoreAndroidSystemSettings?: boolean;
14
+ }
15
+ ) => void;
16
+ };
17
+
18
+ function loadExpoHaptics(): ExpoHapticsModule | null {
19
+ try {
20
+ return require('expo-haptics') as ExpoHapticsModule;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function loadRNHapticFeedback(): RNHapticFeedbackModule | null {
27
+ try {
28
+ return require('react-native-haptic-feedback') as RNHapticFeedbackModule;
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+
34
+ const expoHaptics = loadExpoHaptics();
35
+ const rnHapticFeedback = loadRNHapticFeedback();
36
+ const rnHapticsAvailable =
37
+ hasNativeModule('RNHapticFeedback', 'RNReactNativeHapticFeedback') &&
38
+ typeof rnHapticFeedback?.trigger === 'function';
39
+
40
+ export function triggerSelectionHaptic(): void {
41
+ if (Platform.OS === 'web') return;
42
+
43
+ try {
44
+ if (typeof expoHaptics?.selectionAsync === 'function') {
45
+ void expoHaptics.selectionAsync();
46
+ return;
47
+ }
48
+
49
+ if (rnHapticsAvailable) {
50
+ rnHapticFeedback?.trigger?.('selection', {
51
+ enableVibrateFallback: false,
52
+ ignoreAndroidSystemSettings: false,
53
+ });
54
+ return;
55
+ }
56
+
57
+ if (Platform.OS === 'android') {
58
+ Vibration.vibrate(4);
59
+ return;
60
+ }
61
+
62
+ warnOnce(
63
+ 'native-haptics-missing-ios',
64
+ 'No native haptics package is installed. Selection haptics are disabled on iOS to avoid heavy vibration.'
65
+ );
66
+ } catch {
67
+ warnOnce(
68
+ 'native-haptics-error',
69
+ 'Failed to trigger haptics. Flowboard will continue without tactile feedback.'
70
+ );
71
+ }
72
+ }