@getuserfeedback/react-native 5.14.1 → 5.15.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/dist/native-core-command-session.js +9 -1
- package/dist/native-flow-content.d.ts +8 -0
- package/dist/native-flow-content.js +160 -0
- package/dist/native-link-router-rpc.js +5 -1
- package/dist/native-provider.js +11 -9
- package/dist/native-view-host-projection.d.ts +2 -1
- package/dist/native-view-host-projection.js +9 -1
- package/dist/native-view-presentation-surface.js +29 -8
- package/dist/native-view-record-controller.d.ts +28 -0
- package/dist/native-view-record-controller.js +228 -41
- package/dist/provider-command-helpers.js +13 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -12,7 +12,15 @@ class NativeCoreCommandSessionClosedError extends Error {
|
|
|
12
12
|
function toCommandSettledDetail(settlement) {
|
|
13
13
|
var _a, _b;
|
|
14
14
|
if (settlement.ok) {
|
|
15
|
-
|
|
15
|
+
const detail = {
|
|
16
|
+
instanceId: (_a = settlement.instanceId) !== null && _a !== void 0 ? _a : null,
|
|
17
|
+
kind: settlement.kind,
|
|
18
|
+
ok: true,
|
|
19
|
+
requestId: settlement.requestId,
|
|
20
|
+
};
|
|
21
|
+
if (settlement.result !== undefined)
|
|
22
|
+
detail.result = settlement.result;
|
|
23
|
+
return detail;
|
|
16
24
|
}
|
|
17
25
|
return {
|
|
18
26
|
error: settlement.error,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { NativeViewPresentationMode } from "./native-view-presentation-surface.js";
|
|
2
|
+
import type { NativeViewRecord, NativeViewRecordController } from "./native-view-record-controller.js";
|
|
3
|
+
/** Thin native editor: drafts only. Core owns validation, navigation and submission. */
|
|
4
|
+
export declare function NativeFlowContent({ controller, presentationMode, record, }: {
|
|
5
|
+
controller: NativeViewRecordController;
|
|
6
|
+
presentationMode?: NativeViewPresentationMode;
|
|
7
|
+
record: NativeViewRecord;
|
|
8
|
+
}): import("react").ReactElement<Record<string, unknown>, string | import("react").JSXElementConstructor<any>>;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createElement, useLayoutEffect, useRef, useState, } from "react";
|
|
2
|
+
/** Thin native editor: drafts only. Core owns validation, navigation and submission. */
|
|
3
|
+
export function NativeFlowContent({ controller, presentationMode = "overlay", record, }) {
|
|
4
|
+
const flow = record.nativeFlow;
|
|
5
|
+
if (!flow)
|
|
6
|
+
throw new Error("Native content requires a native allocation");
|
|
7
|
+
const { projection, interaction } = flow;
|
|
8
|
+
const { View, Text, TextInput, Pressable, ScrollView, useWindowDimensions, } = require("react-native");
|
|
9
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
10
|
+
const [draft, setDraft] = useState(projection.answers);
|
|
11
|
+
const rawDraft = useRef(draft);
|
|
12
|
+
const scroll = useRef(null);
|
|
13
|
+
const editorPage = useRef({
|
|
14
|
+
allocationId: record.allocationId,
|
|
15
|
+
nodeId: projection.nodeId,
|
|
16
|
+
});
|
|
17
|
+
const binding = useRef(null);
|
|
18
|
+
useLayoutEffect(() => {
|
|
19
|
+
const mounted = controller.attachNativeContent({
|
|
20
|
+
viewId: record.viewId,
|
|
21
|
+
allocationId: record.allocationId,
|
|
22
|
+
});
|
|
23
|
+
binding.current = mounted;
|
|
24
|
+
return () => {
|
|
25
|
+
binding.current = null;
|
|
26
|
+
mounted === null || mounted === void 0 ? void 0 : mounted.dispose();
|
|
27
|
+
};
|
|
28
|
+
}, [controller, record.viewId, record.allocationId]);
|
|
29
|
+
useLayoutEffect(() => {
|
|
30
|
+
var _a;
|
|
31
|
+
if (editorPage.current.allocationId === record.allocationId &&
|
|
32
|
+
editorPage.current.nodeId === projection.nodeId)
|
|
33
|
+
return;
|
|
34
|
+
editorPage.current = {
|
|
35
|
+
allocationId: record.allocationId,
|
|
36
|
+
nodeId: projection.nodeId,
|
|
37
|
+
};
|
|
38
|
+
rawDraft.current = projection.answers;
|
|
39
|
+
setDraft(projection.answers);
|
|
40
|
+
(_a = scroll.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: 0, y: 0, animated: false });
|
|
41
|
+
}, [projection, record.allocationId]);
|
|
42
|
+
const update = (key, value) => {
|
|
43
|
+
const nextDraft = Object.assign(Object.assign({}, rawDraft.current), { [key]: value });
|
|
44
|
+
if (!controller.interactNativeFlow({
|
|
45
|
+
viewId: record.viewId,
|
|
46
|
+
allocationId: record.allocationId,
|
|
47
|
+
projection,
|
|
48
|
+
intent: "draft",
|
|
49
|
+
draft: nextDraft,
|
|
50
|
+
}))
|
|
51
|
+
return;
|
|
52
|
+
rawDraft.current = nextDraft;
|
|
53
|
+
setDraft(rawDraft.current);
|
|
54
|
+
};
|
|
55
|
+
const interact = (intent) => controller.interactNativeFlow({
|
|
56
|
+
viewId: record.viewId,
|
|
57
|
+
allocationId: record.allocationId,
|
|
58
|
+
projection,
|
|
59
|
+
intent,
|
|
60
|
+
draft: Object.assign({}, rawDraft.current),
|
|
61
|
+
});
|
|
62
|
+
const disabled = interaction === "action" || !record.isActive;
|
|
63
|
+
const button = (label, onPress, buttonDisabled = interaction !== "idle" || !record.isActive) => createElement(Pressable, {
|
|
64
|
+
key: label,
|
|
65
|
+
accessibilityRole: "button",
|
|
66
|
+
accessibilityLabel: label,
|
|
67
|
+
disabled: buttonDisabled,
|
|
68
|
+
onPress,
|
|
69
|
+
style: {
|
|
70
|
+
padding: 12,
|
|
71
|
+
backgroundColor: buttonDisabled ? "#ddd" : "#e7eefb",
|
|
72
|
+
borderRadius: 6,
|
|
73
|
+
marginTop: 8,
|
|
74
|
+
},
|
|
75
|
+
}, createElement(Text, { style: { color: "#17202a" } }, label));
|
|
76
|
+
return createElement(ScrollView, {
|
|
77
|
+
ref: scroll,
|
|
78
|
+
style: Object.assign(Object.assign({}, (presentationMode === "content"
|
|
79
|
+
? { flexGrow: 0, flexShrink: 1, maxHeight: windowHeight }
|
|
80
|
+
: { flex: 1, maxHeight: "100%" })), { width: "100%", backgroundColor: "#fff" }),
|
|
81
|
+
keyboardShouldPersistTaps: "handled",
|
|
82
|
+
nestedScrollEnabled: presentationMode === "content",
|
|
83
|
+
// Report the actual viewport, never the window ceiling or inner content.
|
|
84
|
+
// Content mode can measure before its auto-height parent has any height.
|
|
85
|
+
onLayout: (event) => { var _a; return (_a = binding.current) === null || _a === void 0 ? void 0 : _a.onLayout(event.nativeEvent.layout); },
|
|
86
|
+
}, createElement(View, {
|
|
87
|
+
testID: "native-flow-renderer",
|
|
88
|
+
style: { padding: 24, gap: 12 },
|
|
89
|
+
}, projection.mode === "simulated"
|
|
90
|
+
? createElement(Text, { testID: "native-flow-simulated" }, "SIMULATED — no response is saved")
|
|
91
|
+
: null, ...projection.blocks.map((block) => {
|
|
92
|
+
var _a;
|
|
93
|
+
if (block.kind === "statement")
|
|
94
|
+
return createElement(View, { key: block.key }, ...block.paragraphs.map((text, index) => createElement(Text, { key: index }, text)));
|
|
95
|
+
const value = draft[block.key];
|
|
96
|
+
const requiredLabel = block.required ? ", required" : "";
|
|
97
|
+
const selected = Array.isArray(value)
|
|
98
|
+
? ((_a = value[0]) !== null && _a !== void 0 ? _a : "")
|
|
99
|
+
: (value !== null && value !== void 0 ? value : "");
|
|
100
|
+
return createElement(View, { key: block.key, style: { gap: 6 } }, createElement(Text, {}, block.label + (block.required ? " *" : "")), block.description ? createElement(Text, {}, block.description) : null, block.kind === "text"
|
|
101
|
+
? createElement(TextInput, {
|
|
102
|
+
accessibilityLabel: block.label + requiredLabel,
|
|
103
|
+
accessibilityHint: block.description,
|
|
104
|
+
value: selected,
|
|
105
|
+
placeholder: block.placeholder,
|
|
106
|
+
editable: !disabled,
|
|
107
|
+
maxLength: 4000,
|
|
108
|
+
onChangeText: (text) => update(block.key, text),
|
|
109
|
+
style: {
|
|
110
|
+
color: "#17202a",
|
|
111
|
+
borderWidth: 1,
|
|
112
|
+
borderColor: "#999",
|
|
113
|
+
borderRadius: 4,
|
|
114
|
+
padding: 10,
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
: createElement(View, {
|
|
118
|
+
accessibilityRole: "radiogroup",
|
|
119
|
+
accessibilityLabel: block.label + requiredLabel,
|
|
120
|
+
accessibilityHint: block.description,
|
|
121
|
+
accessible: false,
|
|
122
|
+
}, ...block.options.map((option) => createElement(Pressable, {
|
|
123
|
+
key: option.value,
|
|
124
|
+
accessibilityRole: "radio",
|
|
125
|
+
accessibilityLabel: `${block.label}: ${option.label}${requiredLabel}`,
|
|
126
|
+
accessibilityHint: block.description,
|
|
127
|
+
accessible: true,
|
|
128
|
+
accessibilityState: {
|
|
129
|
+
checked: selected === option.value,
|
|
130
|
+
disabled,
|
|
131
|
+
},
|
|
132
|
+
disabled,
|
|
133
|
+
onPress: () => update(block.key, option.value),
|
|
134
|
+
style: { padding: 10 },
|
|
135
|
+
}, createElement(Text, {}, `${selected === option.value ? "●" : "○"} ${option.label}`)))), projection.errors[block.key]
|
|
136
|
+
? createElement(Text, { accessibilityRole: "alert", style: { color: "#a00" } }, projection.errors[block.key])
|
|
137
|
+
: null);
|
|
138
|
+
}), ...projection.formErrors.map((text, index) => createElement(Text, { key: `error-${index}`, accessibilityRole: "alert" }, text)), ...projection.terminal.map((text, index) => createElement(Text, { key: `terminal-${index}` }, text)), projection.status === "succeeded" &&
|
|
139
|
+
(projection.mode === "simulated" || projection.terminal.length === 0)
|
|
140
|
+
? createElement(Text, { testID: "native-flow-complete" }, projection.mode === "simulated"
|
|
141
|
+
? "SIMULATED completion — nothing saved"
|
|
142
|
+
: "Thank you")
|
|
143
|
+
: null, projection.canGoBack ? button("Back", () => interact("back")) : null, interaction === "draft-failed"
|
|
144
|
+
? button("Retry refresh", () => interact("draft"), !record.isActive)
|
|
145
|
+
: null, projection.status !== "succeeded"
|
|
146
|
+
? button(interaction === "action"
|
|
147
|
+
? "Working…"
|
|
148
|
+
: projection.status === "ready-to-submit"
|
|
149
|
+
? "Submit"
|
|
150
|
+
: "Continue", () => interact(projection.status === "ready-to-submit" ? "submit" : "continue"))
|
|
151
|
+
: null, projection.canClose
|
|
152
|
+
? createElement(Pressable, {
|
|
153
|
+
accessibilityRole: "button",
|
|
154
|
+
accessibilityLabel: "Close",
|
|
155
|
+
disabled: !record.isActive,
|
|
156
|
+
onPress: () => controller.closeNativeFlow(record),
|
|
157
|
+
style: { padding: 12 },
|
|
158
|
+
}, createElement(Text, {}, "Close"))
|
|
159
|
+
: null));
|
|
160
|
+
}
|
|
@@ -138,7 +138,11 @@ export const createNativeLinkRouterRpcHandler = (input) => {
|
|
|
138
138
|
}
|
|
139
139
|
let handled = false;
|
|
140
140
|
try {
|
|
141
|
-
|
|
141
|
+
const linkRequest = { url: params.url };
|
|
142
|
+
if (params.target !== undefined) {
|
|
143
|
+
linkRequest.target = params.target;
|
|
144
|
+
}
|
|
145
|
+
invokeLinkRouter(router, linkRequest);
|
|
142
146
|
handled = true;
|
|
143
147
|
}
|
|
144
148
|
catch (error) {
|
package/dist/native-provider.js
CHANGED
|
@@ -87,15 +87,17 @@ const toInitFlags = (flags) => {
|
|
|
87
87
|
const parseNativeInitOptions = (_a, hostActions) => {
|
|
88
88
|
var { actions: _actions, capabilities, flags, links: _links } = _a, clientOptions = __rest(_a, ["actions", "capabilities", "flags", "links"]);
|
|
89
89
|
const normalizedFlags = toInitFlags(flags);
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
: {}))
|
|
90
|
+
const initInput = Object.assign({}, clientOptions);
|
|
91
|
+
if (normalizedFlags !== undefined) {
|
|
92
|
+
initInput.flags = normalizedFlags;
|
|
93
|
+
}
|
|
94
|
+
if (hostActions !== undefined && hostActions.length > 0) {
|
|
95
|
+
initInput.hostActions = hostActions;
|
|
96
|
+
}
|
|
97
|
+
if (capabilities !== undefined) {
|
|
98
|
+
initInput.hostCapabilities = capabilities.map((capability) => typeof capability === "string" ? { key: capability } : Object.assign({}, capability));
|
|
99
|
+
}
|
|
100
|
+
const initOptions = parseInitOptions(initInput);
|
|
99
101
|
const hostCapabilities = initOptions.hostCapabilities;
|
|
100
102
|
const normalizedHostCapabilities = hostCapabilities === null || hostCapabilities === void 0 ? void 0 : hostCapabilities.map((capability) => ({
|
|
101
103
|
capability,
|
|
@@ -11,9 +11,10 @@ export type NativeViewHostProjectionProps = {
|
|
|
11
11
|
export type NativeViewProjection = Readonly<Omit<NativeViewHostProjectionProps, "presentationMode"> & {
|
|
12
12
|
key: number;
|
|
13
13
|
}>;
|
|
14
|
-
export declare function NativeProjectedViewContent({ controller, onInvalidMessage, record, webViewComponent, }: {
|
|
14
|
+
export declare function NativeProjectedViewContent({ controller, onInvalidMessage, presentationMode, record, webViewComponent, }: {
|
|
15
15
|
controller: NativeViewRecordController;
|
|
16
16
|
onInvalidMessage?: (error: Error, value: unknown) => void;
|
|
17
|
+
presentationMode?: NativeViewPresentationMode;
|
|
17
18
|
record: NativeViewRecord;
|
|
18
19
|
webViewComponent?: ComponentType<Record<string, unknown>>;
|
|
19
20
|
}): ReactElement;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createElement, Fragment, useCallback, useEffect, useLayoutEffect, useRef, useSyncExternalStore, } from "react";
|
|
2
|
+
import { NativeFlowContent } from "./native-flow-content.js";
|
|
2
3
|
import { NativeViewPresentationSurface } from "./native-view-presentation-surface.js";
|
|
3
4
|
import { ViewWebViewHost } from "./view-webview-host.js";
|
|
4
|
-
export function NativeProjectedViewContent({ controller, onInvalidMessage, record, webViewComponent, }) {
|
|
5
|
+
export function NativeProjectedViewContent({ controller, onInvalidMessage, presentationMode, record, webViewComponent, }) {
|
|
5
6
|
const detachConnectionRef = useRef(null);
|
|
6
7
|
const detachConnection = useCallback(() => {
|
|
7
8
|
const detach = detachConnectionRef.current;
|
|
@@ -18,6 +19,12 @@ export function NativeProjectedViewContent({ controller, onInvalidMessage, recor
|
|
|
18
19
|
}
|
|
19
20
|
}, [controller, record.allocationId]);
|
|
20
21
|
useEffect(() => detachConnection, [detachConnection]);
|
|
22
|
+
if (record.nativeFlow)
|
|
23
|
+
return createElement(NativeFlowContent, {
|
|
24
|
+
controller,
|
|
25
|
+
record,
|
|
26
|
+
presentationMode,
|
|
27
|
+
});
|
|
21
28
|
return createElement(ViewWebViewHost, {
|
|
22
29
|
generation: record.generation,
|
|
23
30
|
onActivationRetryExhausted: (error) => {
|
|
@@ -76,6 +83,7 @@ function NativeViewHostProjectionEntry({ controller, nativeViewComponent, onInva
|
|
|
76
83
|
}, createElement(NativeProjectedViewContent, {
|
|
77
84
|
controller,
|
|
78
85
|
onInvalidMessage,
|
|
86
|
+
presentationMode,
|
|
79
87
|
record,
|
|
80
88
|
webViewComponent,
|
|
81
89
|
}));
|
|
@@ -24,13 +24,24 @@ const CONTENT_HOST_SURFACE_STYLE = {
|
|
|
24
24
|
maxHeight: "100%",
|
|
25
25
|
maxWidth: "100%",
|
|
26
26
|
};
|
|
27
|
+
const NATIVE_CONTENT_PREPARATION_STYLE = {
|
|
28
|
+
position: "absolute",
|
|
29
|
+
top: 0,
|
|
30
|
+
left: 0,
|
|
31
|
+
right: 0,
|
|
32
|
+
opacity: 0,
|
|
33
|
+
};
|
|
34
|
+
const NATIVE_CONTENT_FRAME_STYLE = {
|
|
35
|
+
flexShrink: 1,
|
|
36
|
+
width: "100%",
|
|
37
|
+
};
|
|
27
38
|
const TOP_PLACEMENT_STYLE = {
|
|
28
39
|
justifyContent: "flex-start",
|
|
29
40
|
};
|
|
30
41
|
const BOTTOM_PLACEMENT_STYLE = {
|
|
31
42
|
justifyContent: "flex-end",
|
|
32
43
|
};
|
|
33
|
-
const
|
|
44
|
+
const FILL_AVAILABLE_BOUNDS_STYLE = {
|
|
34
45
|
height: "100%",
|
|
35
46
|
width: "100%",
|
|
36
47
|
};
|
|
@@ -63,9 +74,11 @@ export function NativeViewPresentationSurface({ children, presentationMode = "ov
|
|
|
63
74
|
var _a;
|
|
64
75
|
const NativeView = nativeViewComponent !== null && nativeViewComponent !== void 0 ? nativeViewComponent : resolveNativeView();
|
|
65
76
|
const isPresented = record.isActive && record.size !== null;
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
const isNativeContent = presentationMode === "content" && !!record.nativeFlow;
|
|
78
|
+
const hostStyle = isNativeContent && !isPresented
|
|
79
|
+
? NATIVE_CONTENT_PREPARATION_STYLE
|
|
80
|
+
: presentationMode === "content" && isPresented
|
|
81
|
+
? Object.assign(Object.assign({}, CONTENT_HOST_SURFACE_STYLE), (isNativeContent ? NATIVE_CONTENT_FRAME_STYLE : {})) : Object.assign(Object.assign(Object.assign({}, HOST_SURFACE_STYLE), (((_a = record.size) === null || _a === void 0 ? void 0 : _a.placement) === "docked-top"
|
|
69
82
|
? TOP_PLACEMENT_STYLE
|
|
70
83
|
: BOTTOM_PLACEMENT_STYLE)), (isPresented
|
|
71
84
|
? PRESENTED_HOST_SURFACE_STYLE
|
|
@@ -78,16 +91,20 @@ export function NativeViewPresentationSurface({ children, presentationMode = "ov
|
|
|
78
91
|
testID: `gx-native-view-presentation-${record.viewId}`,
|
|
79
92
|
}, createElement(NativeViewPresentationFrame, {
|
|
80
93
|
children,
|
|
94
|
+
isNativeContent,
|
|
81
95
|
nativeViewComponent,
|
|
82
96
|
record,
|
|
83
97
|
transitionSchedule,
|
|
84
98
|
}));
|
|
85
99
|
}
|
|
86
|
-
function NativeViewPresentationFrame({ children, nativeViewComponent, record, transitionSchedule, }) {
|
|
100
|
+
function NativeViewPresentationFrame({ children, isNativeContent, nativeViewComponent, record, transitionSchedule, }) {
|
|
87
101
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
88
102
|
const NativeView = nativeViewComponent !== null && nativeViewComponent !== void 0 ? nativeViewComponent : resolveNativeView();
|
|
89
103
|
const isPresented = record.isActive && record.size !== null;
|
|
90
|
-
|
|
104
|
+
// Native size reports describe this frame's viewport, not a content-size
|
|
105
|
+
// request. Keep it host-relative so a smaller layout cannot cap later growth.
|
|
106
|
+
const hasMeasuredFrame = !record.nativeFlow &&
|
|
107
|
+
(isPresented || ((_a = record.size) === null || _a === void 0 ? void 0 : _a.layoutTransactionId) !== undefined);
|
|
91
108
|
const cornerRadii = toNativeWidgetCornerRadii((_b = record.size) === null || _b === void 0 ? void 0 : _b.frameAppearance);
|
|
92
109
|
const shadow = toNativeWidgetFrameShadow(((_d = (_c = record.size) === null || _c === void 0 ? void 0 : _c.frameAppearance) === null || _d === void 0 ? void 0 : _d.kind) === "page"
|
|
93
110
|
? record.size.frameAppearance.resolvedBoxShadow
|
|
@@ -108,9 +125,13 @@ function NativeViewPresentationFrame({ children, nativeViewComponent, record, tr
|
|
|
108
125
|
};
|
|
109
126
|
const targetStyles = {
|
|
110
127
|
border: Object.assign(Object.assign(Object.assign(Object.assign({}, VIEW_FRAME_BORDER_OVERLAY_STYLE), cornerRadiusStyle), borderStyle), frameOverlayShadowStyle),
|
|
111
|
-
content: Object.assign(Object.assign({},
|
|
128
|
+
content: Object.assign(Object.assign({}, (isNativeContent
|
|
129
|
+
? { flexShrink: 1, overflow: "hidden" }
|
|
130
|
+
: VIEW_FRAME_CONTENT_STYLE)), cornerRadiusStyle),
|
|
112
131
|
frame: hasMeasuredFrame
|
|
113
|
-
? Object.assign(Object.assign(Object.assign(Object.assign({}, MEASURED_VIEW_FRAME_STYLE), cornerRadiusStyle), frameShadowStyle), { height: (_g = record.size) === null || _g === void 0 ? void 0 : _g.height, width: (_h = record.size) === null || _h === void 0 ? void 0 : _h.width }) :
|
|
132
|
+
? Object.assign(Object.assign(Object.assign(Object.assign({}, MEASURED_VIEW_FRAME_STYLE), cornerRadiusStyle), frameShadowStyle), { height: (_g = record.size) === null || _g === void 0 ? void 0 : _g.height, width: (_h = record.size) === null || _h === void 0 ? void 0 : _h.width }) : isNativeContent
|
|
133
|
+
? NATIVE_CONTENT_FRAME_STYLE
|
|
134
|
+
: FILL_AVAILABLE_BOUNDS_STYLE,
|
|
114
135
|
};
|
|
115
136
|
const frame = hasMeasuredFrame && record.size
|
|
116
137
|
? {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type WidgetLayoutTransitionSchedule } from "@getuserfeedback/protocol/host";
|
|
2
|
+
import { type NativeFlowInteraction, type NativeFlowProjection } from "@getuserfeedback/protocol/internal/native-flow-session";
|
|
2
3
|
import type { BrowserNavigationAvailabilityPayload } from "@getuserfeedback/protocol/internal/navigation-rpc";
|
|
3
4
|
import { type EventViewSize } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
4
5
|
import type { OpenRequestDispatcher } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
@@ -8,6 +9,11 @@ type NativeViewSizeReport = Omit<EventViewSize, "gen" | "t" | "viewId"> & {
|
|
|
8
9
|
transitionSchedule?: WidgetLayoutTransitionSchedule;
|
|
9
10
|
};
|
|
10
11
|
export type NativeViewRecord = Readonly<{
|
|
12
|
+
nativeFlow?: Readonly<{
|
|
13
|
+
projection: NativeFlowProjection;
|
|
14
|
+
interaction: "idle" | "draft" | "action" | "draft-failed";
|
|
15
|
+
pending: boolean;
|
|
16
|
+
}>;
|
|
11
17
|
allocationId: number;
|
|
12
18
|
flowId: string | null;
|
|
13
19
|
generation: number;
|
|
@@ -18,6 +24,21 @@ export type NativeViewRecord = Readonly<{
|
|
|
18
24
|
viewId: string;
|
|
19
25
|
}>;
|
|
20
26
|
export type NativeViewRecordController = {
|
|
27
|
+
attachNativeContent(input: {
|
|
28
|
+
allocationId: number;
|
|
29
|
+
viewId: string;
|
|
30
|
+
}): NativeContentBinding | null;
|
|
31
|
+
interactNativeFlow(input: {
|
|
32
|
+
allocationId: number;
|
|
33
|
+
viewId: string;
|
|
34
|
+
projection: NativeFlowProjection;
|
|
35
|
+
intent: NativeFlowInteraction["intent"];
|
|
36
|
+
draft: NativeFlowInteraction["draft"];
|
|
37
|
+
}): boolean;
|
|
38
|
+
closeNativeFlow(input: {
|
|
39
|
+
allocationId: number;
|
|
40
|
+
viewId: string;
|
|
41
|
+
}): void;
|
|
21
42
|
attachViewConnection(input: {
|
|
22
43
|
allocationId: number;
|
|
23
44
|
connection: ViewWebViewHostConnection;
|
|
@@ -41,6 +62,13 @@ export type NativeViewRecordController = {
|
|
|
41
62
|
getSnapshot(): readonly NativeViewRecord[];
|
|
42
63
|
subscribe(listener: () => void): () => void;
|
|
43
64
|
};
|
|
65
|
+
type NativeContentBinding = {
|
|
66
|
+
onLayout(size: {
|
|
67
|
+
width: number;
|
|
68
|
+
height: number;
|
|
69
|
+
}): void;
|
|
70
|
+
dispose(): void;
|
|
71
|
+
};
|
|
44
72
|
type NativeOpenRequestObserver = (payload: Parameters<OpenRequestDispatcher["dispatch"]>[0]) => ReturnType<OpenRequestDispatcher["dispatch"]>;
|
|
45
73
|
export declare function createNativeViewRecordController(input: {
|
|
46
74
|
coreConnection: CoreWebViewHostConnection;
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import { createWidgetLayoutTransitionSchedule, } from "@getuserfeedback/protocol/host";
|
|
2
2
|
import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions-v3";
|
|
3
|
+
import { actionPrerenderNativeFlowSchema, NATIVE_FLOW_SESSION_RPC_METHOD, nativeFlowInteractionSchema, nativeFlowProjectionSchema, } from "@getuserfeedback/protocol/internal/native-flow-session";
|
|
4
|
+
import { createGeneration } from "@getuserfeedback/protocol/internal/v3-generation";
|
|
5
|
+
import { buildRpcRequest, rpcRequestSchema, rpcResponseSchema, } from "@getuserfeedback/protocol/internal/v3-rpc-messages";
|
|
3
6
|
import { actionRequestCloseViewSchema, actionViewConnectSchema, buildEventLayoutTransactionCommitted, buildEventViewSize, eventViewSizeSchema, } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
4
7
|
import { buildEventViewActivated, buildEventViewActivationDecided, buildEventViewAllocationFailed, buildEventViewClosed, buildEventViewCloseFailed, buildEventViewPrerendered, } from "@getuserfeedback/protocol/internal/view-host-lifecycle-events-v3";
|
|
5
8
|
import { bindViewHostControlEnvelope, bindViewToCoreRelayEnvelope, parseCoreToViewRelayEnvelope, } from "@getuserfeedback/protocol/internal/view-relay-contract";
|
|
6
9
|
import { createViewPreparationReadinessRegistry, createViewPreparationRegistry, } from "./view-orchestration-runtime.js";
|
|
10
|
+
// RPC IDs are UUIDs, unlike command IDs whose non-WebCrypto fallback is prefixed.
|
|
11
|
+
// This is live request correlation, not an authentication token.
|
|
12
|
+
function createNativeInteractionRpcId() {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
if (typeof ((_a = globalThis.crypto) === null || _a === void 0 ? void 0 : _a.randomUUID) === "function")
|
|
15
|
+
return globalThis.crypto.randomUUID();
|
|
16
|
+
const bytes = new Uint8Array(16);
|
|
17
|
+
if (typeof ((_b = globalThis.crypto) === null || _b === void 0 ? void 0 : _b.getRandomValues) === "function")
|
|
18
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
19
|
+
else
|
|
20
|
+
for (let index = 0; index < bytes.length; index++)
|
|
21
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
22
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
23
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
24
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
25
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
26
|
+
}
|
|
7
27
|
const createNativeViewReadinessRegistry = () => createViewPreparationReadinessRegistry();
|
|
8
28
|
class NativeViewCleanupError extends Error {
|
|
9
29
|
constructor(resourceErrors, observerErrors, viewId) {
|
|
@@ -63,6 +83,10 @@ export function createNativeViewRecordController(input) {
|
|
|
63
83
|
if (entries.get(entry.viewId) !== entry) {
|
|
64
84
|
return false;
|
|
65
85
|
}
|
|
86
|
+
if (entry.nativeInteraction &&
|
|
87
|
+
entry.nativeInteraction.kind !== "draft-failed")
|
|
88
|
+
clearTimeout(entry.nativeInteraction.timeout);
|
|
89
|
+
entry.nativeInteraction = undefined;
|
|
66
90
|
const detachViewConnection = entry.viewConnectionDetach;
|
|
67
91
|
const resourceErrors = [];
|
|
68
92
|
const observerErrors = [];
|
|
@@ -226,11 +250,18 @@ export function createNativeViewRecordController(input) {
|
|
|
226
250
|
const handlePrerender = (action) => {
|
|
227
251
|
var _a;
|
|
228
252
|
var _b;
|
|
253
|
+
const srcdoc = action.t === "getuserfeedback:action:prerenderView" ? action.srcdoc : "";
|
|
254
|
+
if (srcdoc === undefined) {
|
|
255
|
+
failCoreConnection(new Error("Native prerenderView requires srcdoc; hosted URLs are unsupported"), "Unsupported native view document source");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
229
258
|
const existing = entries.get(action.viewId);
|
|
230
259
|
if (existing) {
|
|
231
260
|
if (existing.generation !== action.gen ||
|
|
232
261
|
existing.instanceId !== action.instanceId ||
|
|
233
|
-
existing.flowRunId !== action.flowRunId
|
|
262
|
+
existing.flowRunId !== action.flowRunId ||
|
|
263
|
+
!!existing.nativeProjection !==
|
|
264
|
+
(action.t === "getuserfeedback:action:prerenderNativeFlow")) {
|
|
234
265
|
failCoreConnection(new Error(`Conflicting native projection binding ${action.viewId}`), "Conflicting native projection binding");
|
|
235
266
|
}
|
|
236
267
|
return;
|
|
@@ -240,31 +271,11 @@ export function createNativeViewRecordController(input) {
|
|
|
240
271
|
return;
|
|
241
272
|
}
|
|
242
273
|
nextAllocationId += 1;
|
|
243
|
-
const entry = {
|
|
244
|
-
|
|
245
|
-
flowId: ((_a = action.openRequest) === null || _a === void 0 ? void 0 : _a.source) === "command"
|
|
274
|
+
const entry = Object.assign(Object.assign({}, (action.t === "getuserfeedback:action:prerenderNativeFlow"
|
|
275
|
+
? { nativeProjection: action.projection }
|
|
276
|
+
: {})), { allocationId: nextAllocationId, flowId: ((_a = action.openRequest) === null || _a === void 0 ? void 0 : _a.source) === "command"
|
|
246
277
|
? ((_b = action.openRequest.flowId) !== null && _b !== void 0 ? _b : null)
|
|
247
|
-
: null,
|
|
248
|
-
generation: action.gen,
|
|
249
|
-
flowRunId: action.flowRunId,
|
|
250
|
-
instanceId: action.instanceId,
|
|
251
|
-
isActive: false,
|
|
252
|
-
isClosing: false,
|
|
253
|
-
isPreparationCommitted: false,
|
|
254
|
-
isPresentationAcknowledged: false,
|
|
255
|
-
isViewConnected: false,
|
|
256
|
-
isViewRelayDraining: false,
|
|
257
|
-
layoutReportRevision: 0,
|
|
258
|
-
pendingCoreToViewMessages: [],
|
|
259
|
-
preparation,
|
|
260
|
-
retainedSize: null,
|
|
261
|
-
size: null,
|
|
262
|
-
srcdoc: action.srcdoc,
|
|
263
|
-
committedLayoutReportOccurrence: null,
|
|
264
|
-
viewConnection: null,
|
|
265
|
-
viewConnectionDetach: null,
|
|
266
|
-
viewId: action.viewId,
|
|
267
|
-
};
|
|
278
|
+
: null, generation: action.gen, flowRunId: action.flowRunId, instanceId: action.instanceId, isActive: false, isClosing: false, isPreparationCommitted: false, isPresentationAcknowledged: false, isViewConnected: false, isViewRelayDraining: false, layoutReportRevision: 0, pendingCoreToViewMessages: [], preparation, retainedSize: null, size: null, srcdoc, committedLayoutReportOccurrence: null, viewConnection: null, viewConnectionDetach: null, viewId: action.viewId });
|
|
268
279
|
entries.set(action.viewId, entry);
|
|
269
280
|
try {
|
|
270
281
|
preparation.addCleanup(() => {
|
|
@@ -383,6 +394,39 @@ export function createNativeViewRecordController(input) {
|
|
|
383
394
|
if (coreConnectionFailed || disposed) {
|
|
384
395
|
return;
|
|
385
396
|
}
|
|
397
|
+
const nativeAction = actionPrerenderNativeFlowSchema.safeParse(value);
|
|
398
|
+
if (nativeAction.success &&
|
|
399
|
+
nativeAction.data.gen === input.coreConnection.generation) {
|
|
400
|
+
handlePrerender(nativeAction.data);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const response = rpcResponseSchema.safeParse(value);
|
|
404
|
+
if (response.success &&
|
|
405
|
+
response.data.gen === input.coreConnection.generation &&
|
|
406
|
+
response.data.viewId) {
|
|
407
|
+
const entry = entries.get(response.data.viewId);
|
|
408
|
+
const interaction = entry === null || entry === void 0 ? void 0 : entry.nativeInteraction;
|
|
409
|
+
if (entry &&
|
|
410
|
+
interaction &&
|
|
411
|
+
interaction.kind !== "draft-failed" &&
|
|
412
|
+
interaction.rpcId === response.data.rpcId &&
|
|
413
|
+
entry.nativeProjection &&
|
|
414
|
+
!entry.isClosing &&
|
|
415
|
+
entry.isActive) {
|
|
416
|
+
const result = response.data.ok
|
|
417
|
+
? nativeFlowProjectionSchema.safeParse(response.data.result)
|
|
418
|
+
: null;
|
|
419
|
+
if (!(result === null || result === void 0 ? void 0 : result.success)) {
|
|
420
|
+
interaction.fail();
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
clearTimeout(interaction.timeout);
|
|
424
|
+
entry.nativeInteraction = undefined;
|
|
425
|
+
entry.nativeProjection = result.data;
|
|
426
|
+
notify();
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
386
430
|
const action = coreViewHostActionSchema.safeParse(value);
|
|
387
431
|
if (action.success && action.data.gen === input.coreConnection.generation) {
|
|
388
432
|
switch (action.data.t) {
|
|
@@ -403,12 +447,12 @@ export function createNativeViewRecordController(input) {
|
|
|
403
447
|
});
|
|
404
448
|
if (relay) {
|
|
405
449
|
const entry = entries.get(relay.viewId);
|
|
406
|
-
if (entry) {
|
|
450
|
+
if (entry && !entry.nativeProjection) {
|
|
407
451
|
enqueueCoreToViewMessage(entry, relay.message);
|
|
408
452
|
}
|
|
409
453
|
}
|
|
410
454
|
};
|
|
411
|
-
const
|
|
455
|
+
const completePreparationReadiness = (entry, size) => {
|
|
412
456
|
if (disposed || entries.get(entry.viewId) !== entry) {
|
|
413
457
|
return;
|
|
414
458
|
}
|
|
@@ -460,6 +504,144 @@ export function createNativeViewRecordController(input) {
|
|
|
460
504
|
};
|
|
461
505
|
const stopCoreTransport = input.coreConnection.transport.subscribe(handleCoreMessage);
|
|
462
506
|
return {
|
|
507
|
+
attachNativeContent({ allocationId, viewId }) {
|
|
508
|
+
const entry = entries.get(viewId);
|
|
509
|
+
if (disposed ||
|
|
510
|
+
coreConnectionFailed ||
|
|
511
|
+
!(entry === null || entry === void 0 ? void 0 : entry.nativeProjection) ||
|
|
512
|
+
entry.allocationId !== allocationId ||
|
|
513
|
+
entry.isClosing)
|
|
514
|
+
return null;
|
|
515
|
+
const binding = {
|
|
516
|
+
onLayout(size) {
|
|
517
|
+
var _a, _b;
|
|
518
|
+
if (disposed ||
|
|
519
|
+
coreConnectionFailed ||
|
|
520
|
+
entries.get(viewId) !== entry ||
|
|
521
|
+
entry.isClosing ||
|
|
522
|
+
entry.nativeContentBinding !== binding)
|
|
523
|
+
return;
|
|
524
|
+
if (!Number.isFinite(size.width) ||
|
|
525
|
+
!Number.isFinite(size.height) ||
|
|
526
|
+
size.width <= 0 ||
|
|
527
|
+
size.height <= 0 ||
|
|
528
|
+
size.width > 4096 ||
|
|
529
|
+
size.height > 4096)
|
|
530
|
+
return;
|
|
531
|
+
if (((_a = entry.size) === null || _a === void 0 ? void 0 : _a.width) === size.width &&
|
|
532
|
+
((_b = entry.size) === null || _b === void 0 ? void 0 : _b.height) === size.height)
|
|
533
|
+
return;
|
|
534
|
+
entry.size = Object.assign({}, size);
|
|
535
|
+
entry.layoutReportRevision += 1;
|
|
536
|
+
try {
|
|
537
|
+
if (!entry.isPreparationCommitted)
|
|
538
|
+
completePreparationReadiness(entry, entry.size);
|
|
539
|
+
else
|
|
540
|
+
input.coreConnection.transport.postMessage(buildEventViewSize(Object.assign({ gen: entry.generation, viewId }, entry.size)));
|
|
541
|
+
notify();
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
if (entry.isPreparationCommitted)
|
|
545
|
+
failCoreConnection(error, "Native content readiness failed");
|
|
546
|
+
else
|
|
547
|
+
failAllocation(entry, error);
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
dispose() {
|
|
551
|
+
if (entry.nativeContentBinding !== binding)
|
|
552
|
+
return;
|
|
553
|
+
entry.nativeContentBinding = undefined;
|
|
554
|
+
entry.size = null;
|
|
555
|
+
if (!disposed && entries.get(viewId) === entry)
|
|
556
|
+
notify();
|
|
557
|
+
},
|
|
558
|
+
};
|
|
559
|
+
entry.nativeContentBinding = binding;
|
|
560
|
+
return binding;
|
|
561
|
+
},
|
|
562
|
+
interactNativeFlow({ allocationId, viewId, projection, intent, draft }) {
|
|
563
|
+
var _a, _b;
|
|
564
|
+
const entry = entries.get(viewId);
|
|
565
|
+
if (disposed ||
|
|
566
|
+
coreConnectionFailed ||
|
|
567
|
+
!entry ||
|
|
568
|
+
entry.allocationId !== allocationId ||
|
|
569
|
+
entry.isClosing ||
|
|
570
|
+
!entry.isActive ||
|
|
571
|
+
!entry.isPreparationCommitted ||
|
|
572
|
+
!entry.nativeContentBinding ||
|
|
573
|
+
!entry.nativeProjection ||
|
|
574
|
+
(intent === "draft"
|
|
575
|
+
? ((_a = entry.nativeInteraction) === null || _a === void 0 ? void 0 : _a.kind) === "action" ||
|
|
576
|
+
entry.nativeProjection.nodeId !== projection.nodeId
|
|
577
|
+
: entry.nativeInteraction !== undefined ||
|
|
578
|
+
entry.nativeProjection !== projection))
|
|
579
|
+
return false;
|
|
580
|
+
const parsed = nativeFlowInteractionSchema.safeParse({
|
|
581
|
+
flowRunId: entry.flowRunId,
|
|
582
|
+
nodeId: projection.nodeId,
|
|
583
|
+
intent,
|
|
584
|
+
draft,
|
|
585
|
+
});
|
|
586
|
+
if (!parsed.success)
|
|
587
|
+
return false;
|
|
588
|
+
const rpcId = createNativeInteractionRpcId();
|
|
589
|
+
const currentProjection = entry.nativeProjection;
|
|
590
|
+
if (((_b = entry.nativeInteraction) === null || _b === void 0 ? void 0 : _b.kind) === "draft")
|
|
591
|
+
clearTimeout(entry.nativeInteraction.timeout);
|
|
592
|
+
// Match the ordinary RPC budget. Correlation already rejects late replies;
|
|
593
|
+
// Timeout or a failed reply restores editing for an explicit manual retry.
|
|
594
|
+
const settleFailure = () => {
|
|
595
|
+
var _a, _b;
|
|
596
|
+
if (disposed ||
|
|
597
|
+
entries.get(viewId) !== entry ||
|
|
598
|
+
((_a = entry.nativeInteraction) === null || _a === void 0 ? void 0 : _a.kind) === "draft-failed" ||
|
|
599
|
+
((_b = entry.nativeInteraction) === null || _b === void 0 ? void 0 : _b.rpcId) !== rpcId)
|
|
600
|
+
return;
|
|
601
|
+
clearTimeout(entry.nativeInteraction.timeout);
|
|
602
|
+
entry.nativeInteraction =
|
|
603
|
+
intent === "draft" ? { kind: "draft-failed" } : undefined;
|
|
604
|
+
entry.nativeProjection = Object.assign(Object.assign({}, currentProjection), { answers: Object.assign(Object.assign({}, currentProjection.answers), parsed.data.draft), formErrors: ["Something went wrong. Please try again."] });
|
|
605
|
+
notify();
|
|
606
|
+
};
|
|
607
|
+
entry.nativeInteraction = {
|
|
608
|
+
kind: intent === "draft" ? "draft" : "action",
|
|
609
|
+
rpcId,
|
|
610
|
+
fail: settleFailure,
|
|
611
|
+
timeout: setTimeout(settleFailure, 10000),
|
|
612
|
+
};
|
|
613
|
+
try {
|
|
614
|
+
notify();
|
|
615
|
+
input.coreConnection.transport.postMessage(rpcRequestSchema.parse(buildRpcRequest({
|
|
616
|
+
gen: entry.generation,
|
|
617
|
+
viewId,
|
|
618
|
+
rpcId,
|
|
619
|
+
idemKey: rpcId,
|
|
620
|
+
method: NATIVE_FLOW_SESSION_RPC_METHOD,
|
|
621
|
+
params: parsed.data,
|
|
622
|
+
})));
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
failCoreConnection(error, "Native interaction delivery failed");
|
|
626
|
+
}
|
|
627
|
+
return true;
|
|
628
|
+
},
|
|
629
|
+
closeNativeFlow({ allocationId, viewId }) {
|
|
630
|
+
var _a;
|
|
631
|
+
const entry = entries.get(viewId);
|
|
632
|
+
if (disposed ||
|
|
633
|
+
coreConnectionFailed ||
|
|
634
|
+
!entry ||
|
|
635
|
+
entry.allocationId !== allocationId ||
|
|
636
|
+
!((_a = entry.nativeProjection) === null || _a === void 0 ? void 0 : _a.canClose) ||
|
|
637
|
+
!entry.isActive)
|
|
638
|
+
return;
|
|
639
|
+
closeViewFromRequest(entry, {
|
|
640
|
+
t: "getuserfeedback:action:requestCloseView",
|
|
641
|
+
gen: createGeneration(input.coreConnection.generation),
|
|
642
|
+
viewId,
|
|
643
|
+
});
|
|
644
|
+
},
|
|
463
645
|
failViewAllocation({ allocationId, error, viewId }) {
|
|
464
646
|
const entry = entries.get(viewId);
|
|
465
647
|
if (!entry || entry.allocationId !== allocationId) {
|
|
@@ -515,9 +697,11 @@ export function createNativeViewRecordController(input) {
|
|
|
515
697
|
entry.isClosing ||
|
|
516
698
|
!entry.isActive ||
|
|
517
699
|
!entry.isPreparationCommitted ||
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
700
|
+
(entry.nativeProjection
|
|
701
|
+
? !entry.nativeContentBinding
|
|
702
|
+
: !entry.isViewConnected ||
|
|
703
|
+
entry.viewConnection === null ||
|
|
704
|
+
entry.viewConnectionDetach === null) ||
|
|
521
705
|
entry.size === null ||
|
|
522
706
|
entry.isPresentationAcknowledged) {
|
|
523
707
|
return;
|
|
@@ -540,6 +724,7 @@ export function createNativeViewRecordController(input) {
|
|
|
540
724
|
const { viewId } = connection;
|
|
541
725
|
const entry = entries.get(viewId);
|
|
542
726
|
if (!entry ||
|
|
727
|
+
entry.nativeProjection ||
|
|
543
728
|
entry.allocationId !== allocationId ||
|
|
544
729
|
connection.generation !== input.coreConnection.generation) {
|
|
545
730
|
return null;
|
|
@@ -584,7 +769,7 @@ export function createNativeViewRecordController(input) {
|
|
|
584
769
|
return;
|
|
585
770
|
}
|
|
586
771
|
try {
|
|
587
|
-
|
|
772
|
+
completePreparationReadiness(entry, size);
|
|
588
773
|
}
|
|
589
774
|
catch (error) {
|
|
590
775
|
failCoreConnection(error, "Native view readiness delivery failed");
|
|
@@ -777,16 +962,18 @@ export function createNativeViewRecordController(input) {
|
|
|
777
962
|
};
|
|
778
963
|
}
|
|
779
964
|
function toRecord(entry) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
965
|
+
var _a, _b, _c;
|
|
966
|
+
var _d;
|
|
967
|
+
return Object.freeze(Object.assign(Object.assign({}, (entry.nativeProjection
|
|
968
|
+
? {
|
|
969
|
+
nativeFlow: {
|
|
970
|
+
projection: entry.nativeProjection,
|
|
971
|
+
interaction: (_d = (_a = entry.nativeInteraction) === null || _a === void 0 ? void 0 : _a.kind) !== null && _d !== void 0 ? _d : "idle",
|
|
972
|
+
pending: ((_b = entry.nativeInteraction) === null || _b === void 0 ? void 0 : _b.kind) === "draft" ||
|
|
973
|
+
((_c = entry.nativeInteraction) === null || _c === void 0 ? void 0 : _c.kind) === "action",
|
|
974
|
+
},
|
|
975
|
+
}
|
|
976
|
+
: {})), { allocationId: entry.allocationId, flowId: entry.flowId, generation: entry.generation, isActive: entry.isActive, layoutReportRevision: entry.layoutReportRevision, size: entry.size, srcdoc: entry.srcdoc, viewId: entry.viewId }));
|
|
790
977
|
}
|
|
791
978
|
function toViewSizeReport(event, context) {
|
|
792
979
|
var _a;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createCommandRequestId, REACT_NATIVE_WEBVIEW_TRANSPORT_CAPABILITIES, } from "@getuserfeedback/protocol/host";
|
|
2
|
+
import { NATIVE_FLOW_SESSION_PROTOCOL } from "@getuserfeedback/protocol/internal/native-flow-session";
|
|
2
3
|
import { REACT_NATIVE_SDK_VERSION } from "./version.js";
|
|
3
4
|
export const REACT_NATIVE_CLIENT_META = {
|
|
4
5
|
clientName: "@getuserfeedback/react-native",
|
|
5
6
|
clientVersion: REACT_NATIVE_SDK_VERSION,
|
|
6
7
|
loader: "sdk",
|
|
7
8
|
protocolCapabilities: [
|
|
9
|
+
NATIVE_FLOW_SESSION_PROTOCOL,
|
|
8
10
|
REACT_NATIVE_WEBVIEW_TRANSPORT_CAPABILITIES.LAYOUT_TRANSITION_POLICY,
|
|
9
11
|
REACT_NATIVE_WEBVIEW_TRANSPORT_CAPABILITIES.LAYOUT_TRANSITION_SCHEDULE,
|
|
10
12
|
],
|
|
@@ -18,5 +20,15 @@ export function createProviderCommandEnvelope({ clientMeta, command, idempotency
|
|
|
18
20
|
const resolvedIdempotencyKey = (idempotencyKey === null || idempotencyKey === void 0 ? void 0 : idempotencyKey.trim()) || resolvedRequestId;
|
|
19
21
|
const resolvedCommand = clientMeta !== undefined
|
|
20
22
|
? Object.assign(Object.assign({}, command), { opts: Object.assign(Object.assign({}, command.opts), { clientMeta }) }) : command;
|
|
21
|
-
|
|
23
|
+
const envelope = {
|
|
24
|
+
version: "1",
|
|
25
|
+
command: resolvedCommand,
|
|
26
|
+
idempotencyKey: resolvedIdempotencyKey,
|
|
27
|
+
instanceId: resolvedInstanceId,
|
|
28
|
+
requestId: resolvedRequestId,
|
|
29
|
+
};
|
|
30
|
+
if (clientMeta !== undefined) {
|
|
31
|
+
envelope.clientMeta = clientMeta;
|
|
32
|
+
}
|
|
33
|
+
return envelope;
|
|
22
34
|
}
|
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.15.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react-native",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.15.2",
|
|
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": "^
|
|
58
|
-
"@getuserfeedback/sdk": "^0.19.
|
|
57
|
+
"@getuserfeedback/protocol": "^6.0.3",
|
|
58
|
+
"@getuserfeedback/sdk": "^0.19.4",
|
|
59
59
|
"robot3": "^1.2.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|