@getuserfeedback/react-native 2.0.25 → 3.0.2

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/README.md CHANGED
@@ -52,6 +52,8 @@ export function App() {
52
52
 
53
53
  `GetUserFeedbackProvider` renders `WidgetHost` internally, auto-enqueues `init`, measures the React Native window for the hosted WebView viewport, shows the hosted WebView inside a native bottom sheet while a flow is loading or open, and resolves command promises when the WebView transport reports command settlement. Pass either `loaderUrl` or a custom WebView `source` so the hosted WebView has a loader runtime to execute commands. Use `onCommandError` to receive automatic setup failures from `init` or `configure`.
54
54
 
55
+ Custom `source` documents must implement the current WebView transport contract. Provider-owned presentation is driven by `instance:view-host:snapshots-changed`; the legacy aggregate `instance:flow:state-changed:instance` event does not control the native sheet.
56
+
55
57
  `WidgetHost` remains exported for advanced hosts that need direct WebView control.
56
58
 
57
59
  The host message types are re-exported from `@getuserfeedback/protocol/webview-transport` through package-owned aliases so the React Native package stays aligned with the shared WebView transport contract.
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import { createContext, createElement, useCallback, useContext, useEffect, useMe
15
15
  import { useResolvedHostViewport } from "./host-viewport.js";
16
16
  import { toCommandSettlementError, toReactNativeOneShotCommand, } from "./provider-command-helpers.js";
17
17
  import { useProviderCommandTransport } from "./provider-command-transport.js";
18
- import { createProviderWidgetHostLifecycleController, HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY, toProviderWidgetHostSnapshot, } from "./provider-widget-host-lifecycle.js";
18
+ import { createProviderWidgetHostLifecycleController, HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY, } from "./provider-widget-host-lifecycle.js";
19
19
  import { ProviderWidgetHostOverlay, toProviderWidgetHostStyle, toProviderWidgetSheetHostLayoutPlan, } from "./provider-widget-host-overlay.js";
20
20
  import { REACT_NATIVE_SDK_VERSION } from "./version.js";
21
21
  import { WidgetHost, } from "./widget-host.js";
@@ -226,7 +226,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
226
226
  const flowRunsRef = useRef(new Map());
227
227
  const flowRunControllerOptionsRef = useRef(null);
228
228
  const widgetHostLifecycle = widgetHostLifecycleRef.current;
229
- const { clearFlowHandleFromVisibility, getCurrentHostEpoch, isWidgetHostVisible: resolveIsWidgetHostVisible, markNotReady, markReady, replaceViewHostSnapshots, syncHostSourceKey, toHostSnapshotVisibility, toReadyVisibility, } = widgetHostLifecycle;
229
+ const { clearFlowHandleFromVisibility, getCurrentHostEpoch, isWidgetHostVisible: resolveIsWidgetHostVisible, markNotReady, markReady, replaceViewHostSnapshots, syncHostSourceKey, toReadyVisibility, } = widgetHostLifecycle;
230
230
  const createCommandTimeoutError = useCallback((requestId) => new CommandSettlementTimeoutError(requestId), []);
231
231
  const transport = useProviderCommandTransport({
232
232
  commandTimeoutMs,
@@ -437,7 +437,9 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
437
437
  clearIdleFlowHandles();
438
438
  didReissueDeliveredCommands = reissueDeliveredPendingActionCommands();
439
439
  }
440
- setWidgetHostVisibility((visibility) => toReadyVisibility(visibility, event));
440
+ setWidgetHostVisibility((visibility) => readyResult.didResetHostEpoch
441
+ ? HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY
442
+ : toReadyVisibility(visibility, event));
441
443
  enqueueStartupCommands({ force: readyResult.previousReady !== null });
442
444
  startPendingCommandTimeouts();
443
445
  return didReissueDeliveredCommands;
@@ -485,17 +487,6 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
485
487
  }
486
488
  setWidgetHostVisibility((visibility) => clearFlowHandleFromVisibility(visibility, flowHandleId));
487
489
  }, [clearFlowHandleFromVisibility]);
488
- const handleInstanceFlowStateChanged = useCallback((detail) => {
489
- const snapshot = toProviderWidgetHostSnapshot(detail, instanceId);
490
- if (!snapshot) {
491
- return;
492
- }
493
- const visibility = toHostSnapshotVisibility(snapshot);
494
- if (!visibility) {
495
- return;
496
- }
497
- setWidgetHostVisibility(visibility);
498
- }, [instanceId, toHostSnapshotVisibility]);
499
490
  const handleWebMessage = useCallback((message) => {
500
491
  if (message.kind === "hostEvent" &&
501
492
  message.event.name === "instance:handle:invalidated" &&
@@ -504,11 +495,10 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
504
495
  }
505
496
  if (message.kind === "hostEvent" &&
506
497
  message.event.name === "instance:view-host:snapshots-changed") {
507
- replaceViewHostSnapshots(message.event.detail, instanceId);
508
- }
509
- if (message.kind === "hostEvent" &&
510
- message.event.name === "instance:flow:state-changed:instance") {
511
- handleInstanceFlowStateChanged(message.event.detail);
498
+ const visibility = replaceViewHostSnapshots(message.event.detail, instanceId);
499
+ if (visibility) {
500
+ setWidgetHostVisibility(visibility);
501
+ }
512
502
  }
513
503
  if (message.kind === "hostEvent" &&
514
504
  message.event.name === "instance:command:settled") {
@@ -550,7 +540,6 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
550
540
  getCurrentHostEpoch,
551
541
  getPendingCommand,
552
542
  handleFlowHandleInvalidated,
553
- handleInstanceFlowStateChanged,
554
543
  instanceId,
555
544
  onWebMessage,
556
545
  replaceViewHostSnapshots,
@@ -14,25 +14,6 @@ export type ProviderWidgetHostVisibility = {
14
14
  transitionPolicy?: WidgetLayoutTransitionPolicy;
15
15
  transitionPolicySequence?: number;
16
16
  };
17
- type ProviderWidgetHostFlowState = {
18
- height?: number;
19
- flowHandleId?: string;
20
- instanceId: string;
21
- isLoading: boolean;
22
- isOpen: boolean;
23
- shouldRender?: boolean;
24
- transitionPolicy?: WidgetLayoutTransitionPolicy;
25
- width?: number;
26
- };
27
- type ProviderWidgetHostSnapshot = {
28
- flowHandleId?: string;
29
- height?: number;
30
- isLoading: boolean;
31
- isOpen: boolean;
32
- shouldRender: boolean;
33
- transitionPolicy?: WidgetLayoutTransitionPolicy;
34
- width?: number;
35
- };
36
17
  export declare const HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY: ProviderWidgetHostVisibility;
37
18
  export type ProviderWidgetHostLifecycleController = {
38
19
  clearFlowHandleFromVisibility: (visibility: ProviderWidgetHostVisibility, flowHandleId: string) => ProviderWidgetHostVisibility;
@@ -52,14 +33,12 @@ export type ProviderWidgetHostLifecycleController = {
52
33
  didResetHostEpoch: boolean;
53
34
  previousReady: ProviderWidgetHostReadyState | null;
54
35
  };
55
- replaceViewHostSnapshots: (detail: InstanceViewHostSnapshotsChangedDetail, instanceId: string) => boolean;
36
+ replaceViewHostSnapshots: (detail: InstanceViewHostSnapshotsChangedDetail, instanceId: string) => ProviderWidgetHostVisibility | null;
56
37
  syncHostSourceKey: (hostSourceKey: string) => boolean;
57
- toHostSnapshotVisibility: (snapshot: ProviderWidgetHostSnapshot) => ProviderWidgetHostVisibility | null;
58
38
  toReadyVisibility: (visibility: ProviderWidgetHostVisibility, event: {
59
39
  readySequence: number;
60
40
  sourceKey: string;
61
41
  }) => ProviderWidgetHostVisibility;
62
42
  };
63
- export declare function toProviderWidgetHostSnapshot(detail: ProviderWidgetHostFlowState, instanceId: string): ProviderWidgetHostSnapshot | null;
64
43
  export declare function createProviderWidgetHostLifecycleController(initialHostSourceKey: string): ProviderWidgetHostLifecycleController;
65
44
  export {};
@@ -1,25 +1,10 @@
1
- import { cloneViewHostSnapshots, resolveInstanceFlowStateShouldRender, } from "@getuserfeedback/protocol/host";
1
+ import { areViewHostSnapshotsEqual, cloneViewHostSnapshots, } from "@getuserfeedback/protocol/host";
2
2
  export const HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY = {
3
3
  hostSourceVersion: null,
4
4
  isVisible: false,
5
5
  readySequence: null,
6
6
  sourceKey: null,
7
7
  };
8
- export function toProviderWidgetHostSnapshot(detail, instanceId) {
9
- if (detail.instanceId !== instanceId) {
10
- return null;
11
- }
12
- const snapshot = {
13
- flowHandleId: detail.flowHandleId,
14
- height: detail.height,
15
- isLoading: detail.isLoading,
16
- isOpen: detail.isOpen,
17
- shouldRender: resolveInstanceFlowStateShouldRender(detail),
18
- transitionPolicy: detail.transitionPolicy,
19
- width: detail.width,
20
- };
21
- return snapshot;
22
- }
23
8
  export function createProviderWidgetHostLifecycleController(initialHostSourceKey) {
24
9
  let hostEpoch = 0;
25
10
  let hostSourceKey = initialHostSourceKey;
@@ -27,6 +12,27 @@ export function createProviderWidgetHostLifecycleController(initialHostSourceKey
27
12
  let lastReady = null;
28
13
  let transitionPolicySequence = 0;
29
14
  let viewHostSnapshots = [];
15
+ let selectedViewHostSnapshot;
16
+ const toVisibility = (snapshot) => {
17
+ if (lastReady === null) {
18
+ return null;
19
+ }
20
+ let nextTransitionPolicySequence;
21
+ if (snapshot.transitionPolicy !== undefined) {
22
+ transitionPolicySequence += 1;
23
+ nextTransitionPolicySequence = transitionPolicySequence;
24
+ }
25
+ return {
26
+ flowHeight: snapshot.height,
27
+ flowHandleId: snapshot.flowHandleId,
28
+ hostSourceVersion,
29
+ isVisible: snapshot.shouldRender,
30
+ readySequence: lastReady.readySequence,
31
+ sourceKey: lastReady.sourceKey,
32
+ transitionPolicy: snapshot.transitionPolicy,
33
+ transitionPolicySequence: nextTransitionPolicySequence,
34
+ };
35
+ };
30
36
  return {
31
37
  clearFlowHandleFromVisibility: (visibility, flowHandleId) => visibility.flowHandleId === flowHandleId
32
38
  ? Object.assign(Object.assign({}, visibility), { flowHandleId: undefined }) : visibility,
@@ -44,6 +50,7 @@ export function createProviderWidgetHostLifecycleController(initialHostSourceKey
44
50
  }
45
51
  lastReady = null;
46
52
  viewHostSnapshots = [];
53
+ selectedViewHostSnapshot = undefined;
47
54
  return { wasReady };
48
55
  },
49
56
  markReady: (event, { hasDeliveredPendingCommands }) => {
@@ -61,6 +68,7 @@ export function createProviderWidgetHostLifecycleController(initialHostSourceKey
61
68
  if (didResetHostEpoch) {
62
69
  hostEpoch += 1;
63
70
  viewHostSnapshots = [];
71
+ selectedViewHostSnapshot = undefined;
64
72
  }
65
73
  lastReady = Object.assign(Object.assign({}, event), { hostEpoch });
66
74
  return {
@@ -71,10 +79,22 @@ export function createProviderWidgetHostLifecycleController(initialHostSourceKey
71
79
  },
72
80
  replaceViewHostSnapshots: (detail, instanceId) => {
73
81
  if (detail.instanceId !== instanceId || lastReady === null) {
74
- return false;
82
+ return null;
75
83
  }
76
84
  viewHostSnapshots = cloneViewHostSnapshots(detail.snapshots);
77
- return true;
85
+ const nextSelectedSnapshot = viewHostSnapshots[0];
86
+ if (areViewHostSnapshotsEqual(selectedViewHostSnapshot, nextSelectedSnapshot)) {
87
+ return null;
88
+ }
89
+ selectedViewHostSnapshot = nextSelectedSnapshot;
90
+ if (!nextSelectedSnapshot) {
91
+ return toVisibility({
92
+ isLoading: false,
93
+ isOpen: false,
94
+ shouldRender: false,
95
+ });
96
+ }
97
+ return toVisibility(Object.assign(Object.assign({}, nextSelectedSnapshot), { shouldRender: true }));
78
98
  },
79
99
  syncHostSourceKey: (nextHostSourceKey) => {
80
100
  if (hostSourceKey === nextHostSourceKey) {
@@ -84,28 +104,9 @@ export function createProviderWidgetHostLifecycleController(initialHostSourceKey
84
104
  hostSourceVersion += 1;
85
105
  lastReady = null;
86
106
  viewHostSnapshots = [];
107
+ selectedViewHostSnapshot = undefined;
87
108
  return true;
88
109
  },
89
- toHostSnapshotVisibility: (snapshot) => {
90
- if (lastReady === null) {
91
- return null;
92
- }
93
- let nextTransitionPolicySequence;
94
- if (snapshot.transitionPolicy !== undefined) {
95
- transitionPolicySequence += 1;
96
- nextTransitionPolicySequence = transitionPolicySequence;
97
- }
98
- return {
99
- flowHeight: snapshot.height,
100
- flowHandleId: snapshot.flowHandleId,
101
- hostSourceVersion,
102
- isVisible: snapshot.shouldRender,
103
- readySequence: lastReady.readySequence,
104
- sourceKey: lastReady.sourceKey,
105
- transitionPolicy: snapshot.transitionPolicy,
106
- transitionPolicySequence: nextTransitionPolicySequence,
107
- };
108
- },
109
110
  toReadyVisibility: (visibility, event) => {
110
111
  if (!visibility.isVisible ||
111
112
  visibility.sourceKey !== event.sourceKey ||
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@ const reactNativeSdkVersion = typeof __GX_REACT_NATIVE_SDK_VERSION__ === "string
3
3
  : "";
4
4
  // Build scripts patch this fallback to the package version for published artifacts.
5
5
  // Source-linked workspace usage keeps the local fallback.
6
- export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "2.0.25";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.2";
@@ -0,0 +1,54 @@
1
+ // ../view-orchestration/src/view-activation-machine.ts
2
+ import { createMachine, interpret, state, transition } from "robot3";
3
+ var VIEW_ACTIVATION_STATES = {
4
+ idle: true,
5
+ prerendering: true,
6
+ "activation-requested": true,
7
+ "prerendering-activation-requested": true,
8
+ prerendered: true,
9
+ active: true
10
+ };
11
+ function createViewActivationMachine() {
12
+ const states = {
13
+ idle: state(transition("startPrerender", "prerendering"), transition("requestActivation", "activation-requested")),
14
+ prerendering: state(transition("requestActivation", "prerendering-activation-requested"), transition("markPrerendered", "prerendered")),
15
+ "activation-requested": state(transition("startPrerender", "prerendering-activation-requested")),
16
+ "prerendering-activation-requested": state(transition("markPrerendered", "active")),
17
+ prerendered: state(transition("requestActivation", "active")),
18
+ active: state()
19
+ };
20
+ return createMachine(states, () => ({}));
21
+ }
22
+ function createViewActivationController() {
23
+ const machine = createViewActivationMachine();
24
+ const service = interpret(machine, () => {});
25
+ const getState = () => readViewActivationState(service.machine.current);
26
+ const send = (event) => {
27
+ service.send(event);
28
+ };
29
+ return {
30
+ getState,
31
+ startPrerender: () => {
32
+ send({ type: "startPrerender" });
33
+ },
34
+ requestActivation: () => {
35
+ send({ type: "requestActivation" });
36
+ },
37
+ markPrerendered: () => {
38
+ send({ type: "markPrerendered" });
39
+ }
40
+ };
41
+ }
42
+ function readViewActivationState(value) {
43
+ if (typeof value !== "string" || !(value in VIEW_ACTIVATION_STATES)) {
44
+ throw new Error(`Unknown view activation state: ${String(value)}`);
45
+ }
46
+ return value;
47
+ }
48
+ // src/view-orchestration-runtime.ts
49
+ function createViewActivationController2() {
50
+ return createViewActivationController();
51
+ }
52
+ export {
53
+ createViewActivationController2 as createViewActivationController
54
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "2.0.25",
3
+ "version": "3.0.2",
4
4
  "description": "getuserfeedback React Native SDK",
5
5
  "keywords": [
6
6
  "getuserfeedback",
@@ -34,18 +34,18 @@
34
34
  "scripts": {
35
35
  "prepack": "node scripts/prepack.cjs",
36
36
  "postpack": "node scripts/postpack.cjs",
37
- "pack:verify": "node ../../scripts/pack-and-verify.cjs --expect-dependency @getuserfeedback/protocol:../protocol/package.json --expect-dependency @getuserfeedback/sdk:../sdk/package.json",
38
- "publish:dry-run": "node ../../scripts/publish-package.cjs . --expect-dependency @getuserfeedback/protocol:../protocol/package.json --expect-dependency @getuserfeedback/sdk:../sdk/package.json -- --dry-run",
39
- "publish:npm": "node ../../scripts/publish-package.cjs . --expect-dependency @getuserfeedback/protocol:../protocol/package.json --expect-dependency @getuserfeedback/sdk:../sdk/package.json",
37
+ "pack:verify": "bun scripts/verify-packed-runtime.ts",
38
+ "publish:dry-run": "node ../../scripts/publish-package.cjs . --verify-packed-runtime scripts/verify-packed-runtime.ts --expect-dependency @getuserfeedback/protocol:../protocol/package.json --expect-dependency @getuserfeedback/sdk:../sdk/package.json -- --dry-run",
39
+ "publish:npm": "node ../../scripts/publish-package.cjs . --verify-packed-runtime scripts/verify-packed-runtime.ts --expect-dependency @getuserfeedback/protocol:../protocol/package.json --expect-dependency @getuserfeedback/sdk:../sdk/package.json",
40
40
  "build": "bun scripts/build.ts",
41
- "typecheck": "tsc -b tsconfig.json",
41
+ "typecheck": "tsc -p tsconfig.json --noEmit --tsBuildInfoFile tsconfig.typecheck.tsbuildinfo && tsc -p tsconfig-scripts.json",
42
42
  "test": "bun test --dots",
43
43
  "test:changed": "bun test --changed=${TEST_CHANGED_BASE:-HEAD} --pass-with-no-tests --dots",
44
44
  "lint": "biome check ."
45
45
  },
46
46
  "dependencies": {
47
- "@getuserfeedback/protocol": "^3.3.2",
48
- "@getuserfeedback/sdk": "^0.8.30",
47
+ "@getuserfeedback/protocol": "^3.4.2",
48
+ "@getuserfeedback/sdk": "^0.8.33",
49
49
  "robot3": "^1.2.0"
50
50
  },
51
51
  "peerDependencies": {