@getuserfeedback/react-native 5.11.0 → 5.14.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.
- package/README.md +27 -8
- package/dist/client.d.ts +29 -22
- package/dist/core-webview-host.js +1 -1
- package/dist/flow-container.js +1 -4
- package/dist/native-core-command-channel.d.ts +7 -0
- package/dist/native-core-command-channel.js +17 -0
- package/dist/native-flow-container-context.d.ts +3 -0
- package/dist/native-link-router-rpc.d.ts +14 -0
- package/dist/native-link-router-rpc.js +163 -0
- package/dist/native-provider-client-controller.d.ts +1 -0
- package/dist/native-provider-client-controller.js +1 -0
- package/dist/native-provider-client.d.ts +1 -1
- package/dist/native-provider-client.js +0 -2
- package/dist/native-provider-flow-registry.d.ts +8 -1
- package/dist/native-provider-flow-registry.js +46 -3
- package/dist/native-provider.d.ts +3 -1
- package/dist/native-provider.js +29 -3
- package/dist/native-runtime-root.d.ts +8 -2
- package/dist/native-runtime-root.js +32 -7
- package/dist/native-view-host-projection.js +8 -2
- package/dist/native-view-layout-transition.d.ts +25 -0
- package/dist/native-view-layout-transition.js +244 -0
- package/dist/native-view-presentation-surface.d.ts +3 -1
- package/dist/native-view-presentation-surface.js +48 -16
- package/dist/native-view-record-controller.d.ts +7 -1
- package/dist/native-view-record-controller.js +50 -6
- package/dist/use-flow.d.ts +32 -0
- package/dist/use-flow.js +50 -0
- package/dist/version.js +1 -1
- package/dist/view-webview-host.js +1 -1
- package/package.json +4 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createWidgetLayoutTransitionSchedule, } from "@getuserfeedback/protocol/host";
|
|
1
2
|
import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions-v3";
|
|
2
3
|
import { actionRequestCloseViewSchema, actionViewConnectSchema, buildEventLayoutTransactionCommitted, buildEventViewSize, eventViewSizeSchema, } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
3
4
|
import { buildEventViewActivated, buildEventViewActivationDecided, buildEventViewAllocationFailed, buildEventViewClosed, buildEventViewCloseFailed, buildEventViewPrerendered, } from "@getuserfeedback/protocol/internal/view-host-lifecycle-events-v3";
|
|
@@ -223,6 +224,8 @@ export function createNativeViewRecordController(input) {
|
|
|
223
224
|
}
|
|
224
225
|
};
|
|
225
226
|
const handlePrerender = (action) => {
|
|
227
|
+
var _a;
|
|
228
|
+
var _b;
|
|
226
229
|
const existing = entries.get(action.viewId);
|
|
227
230
|
if (existing) {
|
|
228
231
|
if (existing.generation !== action.gen ||
|
|
@@ -239,6 +242,9 @@ export function createNativeViewRecordController(input) {
|
|
|
239
242
|
nextAllocationId += 1;
|
|
240
243
|
const entry = {
|
|
241
244
|
allocationId: nextAllocationId,
|
|
245
|
+
flowId: ((_a = action.openRequest) === null || _a === void 0 ? void 0 : _a.source) === "command"
|
|
246
|
+
? ((_b = action.openRequest.flowId) !== null && _b !== void 0 ? _b : null)
|
|
247
|
+
: null,
|
|
242
248
|
generation: action.gen,
|
|
243
249
|
flowRunId: action.flowRunId,
|
|
244
250
|
instanceId: action.instanceId,
|
|
@@ -272,7 +278,8 @@ export function createNativeViewRecordController(input) {
|
|
|
272
278
|
};
|
|
273
279
|
// Governing contract: docs/specs/2026-08-29-sdk-open-request-interception.md.
|
|
274
280
|
const handleActivate = (action) => {
|
|
275
|
-
var _a;
|
|
281
|
+
var _a, _b;
|
|
282
|
+
var _c;
|
|
276
283
|
const entry = entries.get(action.viewId);
|
|
277
284
|
if (!entry ||
|
|
278
285
|
entry.instanceId !== action.instanceId ||
|
|
@@ -302,6 +309,9 @@ export function createNativeViewRecordController(input) {
|
|
|
302
309
|
return;
|
|
303
310
|
}
|
|
304
311
|
if (!entry.isActive) {
|
|
312
|
+
if (((_b = action.openRequest) === null || _b === void 0 ? void 0 : _b.source) === "command") {
|
|
313
|
+
entry.flowId = (_c = action.openRequest.flowId) !== null && _c !== void 0 ? _c : entry.flowId;
|
|
314
|
+
}
|
|
305
315
|
entry.isActive = true;
|
|
306
316
|
notify();
|
|
307
317
|
}
|
|
@@ -487,6 +497,7 @@ export function createNativeViewRecordController(input) {
|
|
|
487
497
|
connection.transport.postMessage(buildEventLayoutTransactionCommitted({
|
|
488
498
|
gen: entry.generation,
|
|
489
499
|
layoutTransactionId,
|
|
500
|
+
transitionSchedule: entry.size.transitionSchedule,
|
|
490
501
|
viewId,
|
|
491
502
|
}));
|
|
492
503
|
}
|
|
@@ -580,7 +591,7 @@ export function createNativeViewRecordController(input) {
|
|
|
580
591
|
}
|
|
581
592
|
};
|
|
582
593
|
const handleMessage = (value) => {
|
|
583
|
-
var _a;
|
|
594
|
+
var _a, _b;
|
|
584
595
|
if (coreConnectionFailed ||
|
|
585
596
|
detached ||
|
|
586
597
|
disposed ||
|
|
@@ -594,8 +605,24 @@ export function createNativeViewRecordController(input) {
|
|
|
594
605
|
connected.data.viewId === viewId) {
|
|
595
606
|
const wasViewConnected = entry.isViewConnected;
|
|
596
607
|
entry.isViewConnected = true;
|
|
608
|
+
const navigationAvailability = !wasViewConnected
|
|
609
|
+
? (_a = input.getBrowserNavigationAvailability) === null || _a === void 0 ? void 0 : _a.call(input)
|
|
610
|
+
: undefined;
|
|
611
|
+
if (navigationAvailability !== undefined) {
|
|
612
|
+
try {
|
|
613
|
+
connection.transport.postMessage({
|
|
614
|
+
gen: entry.generation,
|
|
615
|
+
payload: navigationAvailability,
|
|
616
|
+
t: "getuserfeedback:event:message",
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
failCoreConnection(error, "Native navigation availability delivery failed");
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
597
624
|
if (entry.size === null &&
|
|
598
|
-
((
|
|
625
|
+
((_b = entry.retainedSize) === null || _b === void 0 ? void 0 : _b.connectionId) === connection.connectionId) {
|
|
599
626
|
entry.size = entry.retainedSize.size;
|
|
600
627
|
const readySize = connectionReadiness.recordSize(entry.size);
|
|
601
628
|
if (readySize) {
|
|
@@ -646,13 +673,18 @@ export function createNativeViewRecordController(input) {
|
|
|
646
673
|
}
|
|
647
674
|
return;
|
|
648
675
|
}
|
|
649
|
-
const
|
|
676
|
+
const layoutReportRevision = entry.layoutReportRevision + 1;
|
|
677
|
+
const size = toViewSizeReport(parsed.data, {
|
|
678
|
+
layoutReportRevision,
|
|
679
|
+
previousSize: entry.size,
|
|
680
|
+
viewId,
|
|
681
|
+
});
|
|
650
682
|
entry.size = size;
|
|
651
683
|
entry.retainedSize = {
|
|
652
684
|
connectionId: connection.connectionId,
|
|
653
685
|
size,
|
|
654
686
|
};
|
|
655
|
-
entry.layoutReportRevision
|
|
687
|
+
entry.layoutReportRevision = layoutReportRevision;
|
|
656
688
|
const readySize = connectionReadiness.recordSize(size);
|
|
657
689
|
if (readySize) {
|
|
658
690
|
deliverReadiness(readySize);
|
|
@@ -747,6 +779,7 @@ export function createNativeViewRecordController(input) {
|
|
|
747
779
|
function toRecord(entry) {
|
|
748
780
|
return Object.freeze({
|
|
749
781
|
allocationId: entry.allocationId,
|
|
782
|
+
flowId: entry.flowId,
|
|
750
783
|
generation: entry.generation,
|
|
751
784
|
isActive: entry.isActive,
|
|
752
785
|
layoutReportRevision: entry.layoutReportRevision,
|
|
@@ -755,7 +788,17 @@ function toRecord(entry) {
|
|
|
755
788
|
viewId: entry.viewId,
|
|
756
789
|
});
|
|
757
790
|
}
|
|
758
|
-
function toViewSizeReport(event) {
|
|
791
|
+
function toViewSizeReport(event, context) {
|
|
792
|
+
var _a;
|
|
793
|
+
const transitionSchedule = context.previousSize !== null &&
|
|
794
|
+
event.layoutTransactionId !== undefined &&
|
|
795
|
+
((_a = event.transitionPolicy) === null || _a === void 0 ? void 0 : _a.kind) === "timed"
|
|
796
|
+
? createWidgetLayoutTransitionSchedule({
|
|
797
|
+
nowEpochMs: Date.now(),
|
|
798
|
+
sequence: context.layoutReportRevision,
|
|
799
|
+
transactionId: `${context.viewId}:${event.layoutTransactionId}`,
|
|
800
|
+
})
|
|
801
|
+
: undefined;
|
|
759
802
|
return {
|
|
760
803
|
width: event.width,
|
|
761
804
|
height: event.height,
|
|
@@ -765,5 +808,6 @@ function toViewSizeReport(event) {
|
|
|
765
808
|
placement: event.placement,
|
|
766
809
|
transitionPolicy: event.transitionPolicy,
|
|
767
810
|
layoutTransactionId: event.layoutTransactionId,
|
|
811
|
+
transitionSchedule,
|
|
768
812
|
};
|
|
769
813
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { OpenFlowOptions } from "./client.js";
|
|
2
|
+
/** Options for the React Native `useFlow` hook. */
|
|
3
|
+
export interface UseFlowOptions {
|
|
4
|
+
/** The flow/survey ID to control. */
|
|
5
|
+
flowId: string;
|
|
6
|
+
/** Prefetch the flow resources when the hook mounts. Default `false`. */
|
|
7
|
+
prefetchOnMount?: boolean;
|
|
8
|
+
/** When true, the flow does not show a close button. */
|
|
9
|
+
hideCloseButton?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Per-open options for the React Native `useFlow` hook. */
|
|
12
|
+
export type UseFlowOpenOptions = Pick<OpenFlowOptions, "metadata">;
|
|
13
|
+
/** State and commands for one React Native on-demand flow. */
|
|
14
|
+
export interface UseFlowReturn {
|
|
15
|
+
/** True when the flow has an active, measured projected view. */
|
|
16
|
+
readonly isOpen: boolean;
|
|
17
|
+
/** True while the runtime is preparing this flow for display. */
|
|
18
|
+
readonly isLoading: boolean;
|
|
19
|
+
/** Open the flow. */
|
|
20
|
+
readonly open: (options?: UseFlowOpenOptions) => Promise<void>;
|
|
21
|
+
/** Close the flow. */
|
|
22
|
+
readonly close: () => Promise<void>;
|
|
23
|
+
/** Prefetch the flow resources without opening it. */
|
|
24
|
+
readonly prefetch: () => Promise<void>;
|
|
25
|
+
/** Prerender the flow without opening it. */
|
|
26
|
+
readonly prerender: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Controls one on-demand flow in the provider's native presentation surface.
|
|
30
|
+
* React Native custom containers remain provider-wide via `useFlowContainer()`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function useFlow(options: UseFlowOptions): UseFlowReturn;
|
package/dist/use-flow.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useSyncExternalStore, } from "react";
|
|
2
|
+
import { NativeFlowContainerContext } from "./native-flow-container-context.js";
|
|
3
|
+
const EMPTY_RECORDS = Object.freeze([]);
|
|
4
|
+
const EMPTY_REGISTRY_RECORDS = Object.freeze([]);
|
|
5
|
+
const subscribeNoop = () => () => { };
|
|
6
|
+
const getEmptyRecords = () => EMPTY_RECORDS;
|
|
7
|
+
const getEmptyRegistryRecords = () => EMPTY_REGISTRY_RECORDS;
|
|
8
|
+
const getFlowRegistryRecord = (flowId, records) => records.find((record) => record.flowId === flowId);
|
|
9
|
+
const deriveFlowState = (flowId, registryRecords, records) => {
|
|
10
|
+
const flow = getFlowRegistryRecord(flowId, registryRecords);
|
|
11
|
+
const isOpen = records.some((record) => record.flowId === flowId && record.isActive && record.size !== null);
|
|
12
|
+
const isLoading = !isOpen &&
|
|
13
|
+
((flow === null || flow === void 0 ? void 0 : flow.isOpening) === true ||
|
|
14
|
+
records.some((record) => record.flowId === flowId && record.isActive && record.size === null));
|
|
15
|
+
return { isLoading, isOpen };
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Controls one on-demand flow in the provider's native presentation surface.
|
|
19
|
+
* React Native custom containers remain provider-wide via `useFlowContainer()`.
|
|
20
|
+
*/
|
|
21
|
+
export function useFlow(options) {
|
|
22
|
+
var _a, _b, _c;
|
|
23
|
+
const context = useContext(NativeFlowContainerContext);
|
|
24
|
+
if (!context) {
|
|
25
|
+
throw new Error("useFlow must be used inside GetUserFeedbackProvider");
|
|
26
|
+
}
|
|
27
|
+
const flowRun = useMemo(() => context.client.flow(options.flowId), [context.client, options.flowId]);
|
|
28
|
+
const registryRecords = useSyncExternalStore(context.flowRegistry.subscribe, context.flowRegistry.getSnapshot, getEmptyRegistryRecords);
|
|
29
|
+
const projection = context.projection;
|
|
30
|
+
const viewRecords = useSyncExternalStore((_a = projection === null || projection === void 0 ? void 0 : projection.controller.subscribe) !== null && _a !== void 0 ? _a : subscribeNoop, (_b = projection === null || projection === void 0 ? void 0 : projection.controller.getSnapshot) !== null && _b !== void 0 ? _b : getEmptyRecords, (_c = projection === null || projection === void 0 ? void 0 : projection.controller.getSnapshot) !== null && _c !== void 0 ? _c : getEmptyRecords);
|
|
31
|
+
const state = useMemo(() => {
|
|
32
|
+
return deriveFlowState(flowRun.flowId, registryRecords, viewRecords);
|
|
33
|
+
}, [flowRun.flowId, registryRecords, viewRecords]);
|
|
34
|
+
const open = useCallback((openOptions) => flowRun.open({
|
|
35
|
+
hideCloseButton: options.hideCloseButton,
|
|
36
|
+
metadata: openOptions === null || openOptions === void 0 ? void 0 : openOptions.metadata,
|
|
37
|
+
}), [flowRun, options.hideCloseButton]);
|
|
38
|
+
const close = useCallback(() => flowRun.close(), [flowRun]);
|
|
39
|
+
const prefetch = useCallback(() => flowRun.prefetch(), [flowRun]);
|
|
40
|
+
const prerender = useCallback(() => flowRun.prerender({ hideCloseButton: options.hideCloseButton }), [flowRun, options.hideCloseButton]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (options.prefetchOnMount) {
|
|
43
|
+
void prefetch().catch(() => { });
|
|
44
|
+
}
|
|
45
|
+
}, [options.prefetchOnMount, prefetch]);
|
|
46
|
+
return Object.assign(Object.assign({}, state), { open,
|
|
47
|
+
close,
|
|
48
|
+
prefetch,
|
|
49
|
+
prerender });
|
|
50
|
+
}
|
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 : "5.
|
|
6
|
+
export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "5.14.0";
|
|
@@ -33,7 +33,7 @@ function ViewWebViewHostDocument({ generation, onConnected, onDisconnected, onAc
|
|
|
33
33
|
const controller = controllerRef.current;
|
|
34
34
|
const buildDocumentStartScript = useMemo(() => () => buildViewWebViewBridgeScript({ bridgeId, phase: "documentStart" }), [bridgeId]);
|
|
35
35
|
const source = useMemo(() => toExternalViewSource({ bridgeId, srcdoc }), [bridgeId, srcdoc]);
|
|
36
|
-
return createElement(
|
|
36
|
+
return createElement(WebViewDocumentHost, {
|
|
37
37
|
buildDocumentStartScript,
|
|
38
38
|
controller,
|
|
39
39
|
parseMessage: parseViewWebViewBridgeMessage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react-native",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.14.0",
|
|
4
4
|
"description": "getuserfeedback React Native SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"lint": "biome check ."
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@getuserfeedback/protocol": "^5.
|
|
58
|
-
"@getuserfeedback/sdk": "^0.
|
|
57
|
+
"@getuserfeedback/protocol": "^5.5.0",
|
|
58
|
+
"@getuserfeedback/sdk": "^0.19.0",
|
|
59
59
|
"robot3": "^1.2.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
@@ -64,6 +64,6 @@
|
|
|
64
64
|
"react-native-webview": ">=13.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"typescript": "^
|
|
67
|
+
"typescript": "^7.0.2"
|
|
68
68
|
}
|
|
69
69
|
}
|