@getuserfeedback/react-native 3.0.81 → 3.0.90
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/index.js +29 -89
- package/dist/native-direct-command-session.js +2 -2
- package/dist/native-provider-client.d.ts +64 -0
- package/dist/native-provider-client.js +76 -0
- package/dist/native-runtime-root.d.ts +2 -1
- package/dist/native-runtime-root.js +2 -1
- package/dist/native-view-host-projection.d.ts +2 -1
- package/dist/native-view-host-projection.js +13 -9
- package/dist/native-view-presentation-surface.d.ts +7 -0
- package/dist/native-view-presentation-surface.js +82 -0
- package/dist/native-view-record-controller.d.ts +2 -0
- package/dist/native-view-record-controller.js +58 -8
- package/dist/native-widget-corner-radii.d.ts +10 -0
- package/dist/native-widget-corner-radii.js +54 -0
- package/dist/native-widget-frame-border.d.ts +7 -0
- package/dist/native-widget-frame-border.js +21 -0
- package/dist/native-widget-frame-shadow.d.ts +30 -0
- package/dist/native-widget-frame-shadow.js +49 -0
- package/dist/provider-widget-border-radius.js +5 -18
- package/dist/provider-widget-frame-appearance.d.ts +5 -34
- package/dist/provider-widget-frame-appearance.js +10 -69
- package/dist/provider-widget-host-overlay.d.ts +2 -1
- package/dist/provider-widget-host-overlay.js +7 -6
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -9,10 +9,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import {
|
|
12
|
+
import { CommandSettlementError, CommandSettlementTimeoutError, createFlowCommandHandleController, getFlowHandleFromSettlementDetail, getWidgetLayoutPlanNowMs, } from "@getuserfeedback/protocol/host";
|
|
13
13
|
import { parseWebViewTransportNativeMessage } from "@getuserfeedback/protocol/webview-transport";
|
|
14
14
|
import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
|
|
15
15
|
import { useResolvedHostViewport } from "./host-viewport.js";
|
|
16
|
+
import { createNativeProviderClient, parseRequiredString, } from "./native-provider-client.js";
|
|
16
17
|
import { createProviderCommandEnvelope, REACT_NATIVE_LOADER_CLIENT_META, toCommandSettlementError, toReactNativeOneShotCommand, } from "./provider-command-helpers.js";
|
|
17
18
|
import { useProviderCommandTransport } from "./provider-command-transport.js";
|
|
18
19
|
import { createProviderWidgetHostLifecycleController, HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY, } from "./provider-widget-host-lifecycle.js";
|
|
@@ -136,13 +137,6 @@ const normalizeOptionalString = (value) => {
|
|
|
136
137
|
const normalized = value.trim();
|
|
137
138
|
return normalized.length > 0 ? normalized : undefined;
|
|
138
139
|
};
|
|
139
|
-
const resolveRequiredString = (name, value) => {
|
|
140
|
-
const normalized = normalizeOptionalString(value);
|
|
141
|
-
if (normalized === undefined) {
|
|
142
|
-
throw new Error(`${name} must be a non-empty string`);
|
|
143
|
-
}
|
|
144
|
-
return normalized;
|
|
145
|
-
};
|
|
146
140
|
const toJsonKey = (value) => JSON.stringify(value);
|
|
147
141
|
const toStableHash = (value) => {
|
|
148
142
|
const json = toJsonKey(value);
|
|
@@ -171,33 +165,6 @@ function resolveReactNativeComponents() {
|
|
|
171
165
|
};
|
|
172
166
|
}
|
|
173
167
|
}
|
|
174
|
-
const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
|
|
175
|
-
if (typeof identifyInput === "string") {
|
|
176
|
-
assertNoSegmentExternalIdsInValueArgument({
|
|
177
|
-
argument: traitsOrOptions,
|
|
178
|
-
commandName: "identify",
|
|
179
|
-
valueArgumentName: "traits",
|
|
180
|
-
callShape: "identify(userId, traits, { externalIds })",
|
|
181
|
-
});
|
|
182
|
-
return {
|
|
183
|
-
kind: "identify",
|
|
184
|
-
userId: resolveRequiredString("userId", identifyInput),
|
|
185
|
-
traits: traitsOrOptions,
|
|
186
|
-
options,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
assertNoSegmentExternalIdsInValueArgument({
|
|
190
|
-
argument: identifyInput,
|
|
191
|
-
commandName: "identify",
|
|
192
|
-
valueArgumentName: "traits",
|
|
193
|
-
callShape: "identify(traits, { externalIds })",
|
|
194
|
-
});
|
|
195
|
-
return {
|
|
196
|
-
kind: "identify",
|
|
197
|
-
traits: identifyInput,
|
|
198
|
-
options: options !== null && options !== void 0 ? options : traitsOrOptions,
|
|
199
|
-
};
|
|
200
|
-
};
|
|
201
168
|
export function GetUserFeedbackProvider({ autoInit = true, children, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, configureOptions, hostViewport, instanceId: instanceIdProp, initOptions, loaderUrl, onCommandError, onInvalidWebMessage, onWebMessage, source, webViewComponent, webViewProps, }) {
|
|
202
169
|
var _a;
|
|
203
170
|
const { View: NativeView } = useMemo(resolveReactNativeComponents, []);
|
|
@@ -383,7 +350,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
|
|
|
383
350
|
reportCommandError,
|
|
384
351
|
};
|
|
385
352
|
const flow = useCallback((flowId) => {
|
|
386
|
-
const normalizedFlowId =
|
|
353
|
+
const normalizedFlowId = parseRequiredString("flowId", flowId);
|
|
387
354
|
const existing = flowRunsRef.current.get(normalizedFlowId);
|
|
388
355
|
if (existing) {
|
|
389
356
|
return existing;
|
|
@@ -541,59 +508,32 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
|
|
|
541
508
|
disposeCommandTransport(new Error(PROVIDER_UNMOUNTED_ERROR_MESSAGE));
|
|
542
509
|
}, [disposeCommandTransport]);
|
|
543
510
|
const getNativeMessages = useCallback(() => nativeMessagesRef.current, [nativeMessagesRef]);
|
|
544
|
-
const contextValue = useMemo(() =>
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
},
|
|
571
|
-
prefetch: (flowId, options) => {
|
|
572
|
-
const normalizedFlowId = resolveRequiredString("flowId", flowId);
|
|
573
|
-
return enqueueCommand({
|
|
574
|
-
kind: "prefetch",
|
|
575
|
-
flowId: normalizedFlowId,
|
|
576
|
-
}, options);
|
|
577
|
-
},
|
|
578
|
-
prerender: (flowId, options, commandOptions) => {
|
|
579
|
-
const normalizedFlowId = resolveRequiredString("flowId", flowId);
|
|
580
|
-
return enqueueCommand({
|
|
581
|
-
kind: "prerender",
|
|
582
|
-
flowId: normalizedFlowId,
|
|
583
|
-
hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
|
|
584
|
-
}, commandOptions);
|
|
585
|
-
},
|
|
586
|
-
flow,
|
|
587
|
-
close: (flowHandleId, options) => enqueueCommand(Object.assign({ kind: "close" }, (flowHandleId === undefined
|
|
588
|
-
? {}
|
|
589
|
-
: {
|
|
590
|
-
flowHandleId: resolveRequiredString("flowHandleId", flowHandleId),
|
|
591
|
-
})), options),
|
|
592
|
-
reset: (options) => enqueueCommand({ kind: "reset" }, options),
|
|
593
|
-
updateHostContext: (context, options) => enqueueCommand({ kind: "updateHostContext", context }, options),
|
|
594
|
-
emitHostSignal: (name, data, options) => enqueueCommand(Object.assign({ kind: "emitHostSignal", name: resolveRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
|
|
595
|
-
handleWebMessage,
|
|
596
|
-
}), [enqueueCommand, flow, getNativeMessages, handleWebMessage, instanceId]);
|
|
511
|
+
const contextValue = useMemo(() => {
|
|
512
|
+
const client = createNativeProviderClient({
|
|
513
|
+
dispatchCommand: enqueueCommand,
|
|
514
|
+
flow,
|
|
515
|
+
instanceId,
|
|
516
|
+
});
|
|
517
|
+
return {
|
|
518
|
+
close: client.close,
|
|
519
|
+
configure: client.configure,
|
|
520
|
+
emitHostSignal: client.emitHostSignal,
|
|
521
|
+
enqueueCommand,
|
|
522
|
+
flow: client.flow,
|
|
523
|
+
handleWebMessage,
|
|
524
|
+
identify: client.identify,
|
|
525
|
+
instanceId: client.instanceId,
|
|
526
|
+
get nativeMessages() {
|
|
527
|
+
return getNativeMessages();
|
|
528
|
+
},
|
|
529
|
+
open: client.open,
|
|
530
|
+
prefetch: client.prefetch,
|
|
531
|
+
prerender: client.prerender,
|
|
532
|
+
reset: client.reset,
|
|
533
|
+
track: client.track,
|
|
534
|
+
updateHostContext: client.updateHostContext,
|
|
535
|
+
};
|
|
536
|
+
}, [enqueueCommand, flow, getNativeMessages, handleWebMessage, instanceId]);
|
|
597
537
|
const previousSheetHostLayoutUpdateRef = useRef(undefined);
|
|
598
538
|
const consumedTransitionPolicySequenceRef = useRef(undefined);
|
|
599
539
|
useEffect(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getFlowHandleFromSettlementDetail } from "@getuserfeedback/protocol/host";
|
|
2
2
|
import { toCoreCommandPayload, } from "@getuserfeedback/protocol/internal/public-core-command";
|
|
3
|
-
// TODO(migration): Route the public provider client through this core-ordered direct session.
|
|
4
|
-
// [from=native-
|
|
3
|
+
// TODO(migration): Route the public provider client through this core-ordered direct session after native per-view presentation is complete.
|
|
4
|
+
// [from=native-view-presentation-behavior] [to=native-provider-direct-session] [scope=slice] [principle=core-owned-flow-lifecycle] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=190]
|
|
5
5
|
export function createNativeDirectCommandSession(input) {
|
|
6
6
|
const dispatchToCore = (envelope, command) => {
|
|
7
7
|
const coreEnvelope = Object.assign(Object.assign({}, envelope), { command: toCoreCommandPayload(command) });
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AppEventExternalId, AppEventJsonValue, PublicCommandPayload } from "@getuserfeedback/protocol";
|
|
2
|
+
import type { ConfigureOptions } from "@getuserfeedback/sdk";
|
|
3
|
+
type IdentifyTraits = Record<string, unknown>;
|
|
4
|
+
type TrackProperties = Record<string, AppEventJsonValue>;
|
|
5
|
+
type OpenCommand = Extract<PublicCommandPayload, {
|
|
6
|
+
kind: "open";
|
|
7
|
+
}>;
|
|
8
|
+
type PrerenderCommand = Extract<PublicCommandPayload, {
|
|
9
|
+
kind: "prerender";
|
|
10
|
+
}>;
|
|
11
|
+
type NativeProviderClientCommand = Exclude<Extract<PublicCommandPayload, {
|
|
12
|
+
kind: "configure" | "identify" | "track" | "open" | "prefetch" | "prerender" | "close" | "reset" | "updateHostContext" | "emitHostSignal";
|
|
13
|
+
}>, OpenCommand> | (Omit<OpenCommand, "container"> & {
|
|
14
|
+
container?: never;
|
|
15
|
+
});
|
|
16
|
+
type IdentifyOptions = {
|
|
17
|
+
externalIds?: AppEventExternalId[];
|
|
18
|
+
};
|
|
19
|
+
type TrackOptions = {
|
|
20
|
+
externalIds?: AppEventExternalId[];
|
|
21
|
+
};
|
|
22
|
+
type OpenOptions = Pick<OpenCommand, "hideCloseButton">;
|
|
23
|
+
type PrerenderOptions = Pick<PrerenderCommand, "hideCloseButton">;
|
|
24
|
+
type OpenFlowOptions = Pick<OpenCommand, "hideCloseButton" | "metadata">;
|
|
25
|
+
type PrerenderFlowOptions = Pick<PrerenderCommand, "hideCloseButton">;
|
|
26
|
+
type HostContext = Extract<PublicCommandPayload, {
|
|
27
|
+
kind: "updateHostContext";
|
|
28
|
+
}>["context"];
|
|
29
|
+
interface NativeProviderCommandOptions {
|
|
30
|
+
idempotencyKey?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ReactNativeFlowRun {
|
|
33
|
+
readonly flowId: string;
|
|
34
|
+
open: (options?: OpenFlowOptions) => Promise<void>;
|
|
35
|
+
prefetch: () => Promise<void>;
|
|
36
|
+
prerender: (options?: PrerenderFlowOptions) => Promise<void>;
|
|
37
|
+
close: () => Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export interface NativeProviderClient<TResult> {
|
|
40
|
+
readonly instanceId: string;
|
|
41
|
+
configure: (opts: ConfigureOptions, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
42
|
+
identify: {
|
|
43
|
+
(userId: string, traits?: IdentifyTraits, options?: IdentifyOptions): Promise<TResult>;
|
|
44
|
+
(traits: IdentifyTraits, placeholder: undefined, options?: IdentifyOptions): Promise<TResult>;
|
|
45
|
+
(traits: IdentifyTraits, options?: IdentifyOptions): Promise<TResult>;
|
|
46
|
+
};
|
|
47
|
+
track: (eventName: string, properties?: TrackProperties, options?: TrackOptions) => Promise<TResult>;
|
|
48
|
+
open: (flowId: string, options?: OpenOptions, commandOptions?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
49
|
+
prefetch: (flowId: string, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
50
|
+
prerender: (flowId: string, options?: PrerenderOptions, commandOptions?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
51
|
+
flow: (flowId: string) => ReactNativeFlowRun;
|
|
52
|
+
close: (flowHandleId?: string, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
53
|
+
reset: (options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
54
|
+
updateHostContext: (context: HostContext, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
55
|
+
emitHostSignal: (name: string, data?: unknown, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
56
|
+
}
|
|
57
|
+
type NativeProviderClientOptions<TResult> = {
|
|
58
|
+
dispatchCommand: (command: NativeProviderClientCommand, options?: NativeProviderCommandOptions) => Promise<TResult>;
|
|
59
|
+
flow: (flowId: string) => ReactNativeFlowRun;
|
|
60
|
+
instanceId: string;
|
|
61
|
+
};
|
|
62
|
+
export declare const parseRequiredString: (name: string, value?: string) => string;
|
|
63
|
+
export declare function createNativeProviderClient<TResult>({ dispatchCommand, flow, instanceId, }: NativeProviderClientOptions<TResult>): NativeProviderClient<TResult>;
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { assertNoSegmentExternalIdsInValueArgument } from "@getuserfeedback/protocol/host";
|
|
2
|
+
export const parseRequiredString = (name, value) => {
|
|
3
|
+
const normalized = value === null || value === void 0 ? void 0 : value.trim();
|
|
4
|
+
if (!normalized) {
|
|
5
|
+
throw new Error(`${name} must be a non-empty string`);
|
|
6
|
+
}
|
|
7
|
+
return normalized;
|
|
8
|
+
};
|
|
9
|
+
const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
|
|
10
|
+
if (typeof identifyInput === "string") {
|
|
11
|
+
assertNoSegmentExternalIdsInValueArgument({
|
|
12
|
+
argument: traitsOrOptions,
|
|
13
|
+
commandName: "identify",
|
|
14
|
+
valueArgumentName: "traits",
|
|
15
|
+
callShape: "identify(userId, traits, { externalIds })",
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
kind: "identify",
|
|
19
|
+
userId: parseRequiredString("userId", identifyInput),
|
|
20
|
+
traits: traitsOrOptions,
|
|
21
|
+
options,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
assertNoSegmentExternalIdsInValueArgument({
|
|
25
|
+
argument: identifyInput,
|
|
26
|
+
commandName: "identify",
|
|
27
|
+
valueArgumentName: "traits",
|
|
28
|
+
callShape: "identify(traits, { externalIds })",
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
kind: "identify",
|
|
32
|
+
traits: identifyInput,
|
|
33
|
+
options: options !== null && options !== void 0 ? options : traitsOrOptions,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export function createNativeProviderClient({ dispatchCommand, flow, instanceId, }) {
|
|
37
|
+
return {
|
|
38
|
+
instanceId,
|
|
39
|
+
configure: (opts, options) => dispatchCommand({ kind: "configure", opts }, options),
|
|
40
|
+
identify: ((identifyInput, traitsOrOptions, options) => dispatchCommand(toIdentifyCommandPayload(identifyInput, traitsOrOptions, options))),
|
|
41
|
+
track: (eventName, properties, options) => {
|
|
42
|
+
assertNoSegmentExternalIdsInValueArgument({
|
|
43
|
+
argument: properties,
|
|
44
|
+
commandName: "track",
|
|
45
|
+
valueArgumentName: "properties",
|
|
46
|
+
callShape: "track(eventName, properties, { externalIds })",
|
|
47
|
+
});
|
|
48
|
+
return dispatchCommand(Object.assign(Object.assign({ kind: "track", origin: "customer", event: parseRequiredString("eventName", eventName) }, (properties === undefined ? {} : { properties })), ((options === null || options === void 0 ? void 0 : options.externalIds) === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: { context: { externalIds: options.externalIds } })));
|
|
51
|
+
},
|
|
52
|
+
open: (flowId, options, commandOptions) => dispatchCommand({
|
|
53
|
+
kind: "open",
|
|
54
|
+
flowId: parseRequiredString("flowId", flowId),
|
|
55
|
+
hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
|
|
56
|
+
}, commandOptions),
|
|
57
|
+
prefetch: (flowId, options) => dispatchCommand({
|
|
58
|
+
kind: "prefetch",
|
|
59
|
+
flowId: parseRequiredString("flowId", flowId),
|
|
60
|
+
}, options),
|
|
61
|
+
prerender: (flowId, options, commandOptions) => dispatchCommand({
|
|
62
|
+
kind: "prerender",
|
|
63
|
+
flowId: parseRequiredString("flowId", flowId),
|
|
64
|
+
hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
|
|
65
|
+
}, commandOptions),
|
|
66
|
+
flow,
|
|
67
|
+
close: (flowHandleId, options) => dispatchCommand(Object.assign({ kind: "close" }, (flowHandleId === undefined
|
|
68
|
+
? {}
|
|
69
|
+
: {
|
|
70
|
+
flowHandleId: parseRequiredString("flowHandleId", flowHandleId),
|
|
71
|
+
})), options),
|
|
72
|
+
reset: (options) => dispatchCommand({ kind: "reset" }, options),
|
|
73
|
+
updateHostContext: (context, options) => dispatchCommand({ kind: "updateHostContext", context }, options),
|
|
74
|
+
emitHostSignal: (name, data, options) => dispatchCommand(Object.assign({ kind: "emitHostSignal", name: parseRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -15,6 +15,7 @@ export type NativeRuntimeRootProps = {
|
|
|
15
15
|
onInvalidMessage?: (error: Error, value: unknown) => void;
|
|
16
16
|
onStartupTerminalError?: (error: Error) => void;
|
|
17
17
|
startupCommandSettlementTimeoutMs?: number;
|
|
18
|
+
viewNativeViewComponent?: NativeViewHostProjectionProps["nativeViewComponent"];
|
|
18
19
|
viewWebViewComponent?: NativeViewHostProjectionProps["webViewComponent"];
|
|
19
20
|
viewWebViewProps?: NativeViewHostProjectionProps["webViewProps"];
|
|
20
21
|
};
|
|
@@ -35,6 +36,6 @@ type NativeProviderRuntimeProps = Omit<NativeRuntimeRootProps, "onCommandSession
|
|
|
35
36
|
instanceId: string;
|
|
36
37
|
onCommandSessionChange?: (session: NativeDirectCommandSession | null) => void;
|
|
37
38
|
};
|
|
38
|
-
export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs, startupCommands, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
|
|
39
|
+
export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs, startupCommands, viewNativeViewComponent, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
|
|
39
40
|
export declare function NativeProviderRuntime({ initOptions, initialConfigureOptions, instanceId, onCommandSessionChange, onError, ...runtimeProps }: NativeProviderRuntimeProps): ReactElement;
|
|
40
41
|
export {};
|
|
@@ -43,7 +43,7 @@ async function dispatchStartupCommands(input) {
|
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, startupCommands, viewWebViewComponent, viewWebViewProps, }) {
|
|
46
|
+
export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, startupCommands, viewNativeViewComponent, viewWebViewComponent, viewWebViewProps, }) {
|
|
47
47
|
const [coreHostRevision, setCoreHostRevision] = useState(0);
|
|
48
48
|
const [session, setSession] = useState(null);
|
|
49
49
|
const sessionRef = useRef(null);
|
|
@@ -213,6 +213,7 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
|
|
|
213
213
|
: createElement(NativeViewHostProjection, {
|
|
214
214
|
controller: session.controller,
|
|
215
215
|
key: session.key,
|
|
216
|
+
nativeViewComponent: viewNativeViewComponent,
|
|
216
217
|
onInvalidMessage,
|
|
217
218
|
webViewComponent: viewWebViewComponent,
|
|
218
219
|
webViewProps: viewWebViewProps,
|
|
@@ -3,7 +3,8 @@ import type { NativeViewRecordController } from "./native-view-record-controller
|
|
|
3
3
|
export type NativeViewHostProjectionProps = {
|
|
4
4
|
controller: NativeViewRecordController;
|
|
5
5
|
onInvalidMessage?: (error: Error, value: unknown) => void;
|
|
6
|
+
nativeViewComponent?: ComponentType<Record<string, unknown>>;
|
|
6
7
|
webViewComponent?: ComponentType<Record<string, unknown>>;
|
|
7
8
|
webViewProps?: Record<string, unknown>;
|
|
8
9
|
};
|
|
9
|
-
export declare function NativeViewHostProjection({ controller, onInvalidMessage, webViewComponent, webViewProps, }: NativeViewHostProjectionProps): ReactElement;
|
|
10
|
+
export declare function NativeViewHostProjection({ controller, nativeViewComponent, onInvalidMessage, webViewComponent, webViewProps, }: NativeViewHostProjectionProps): ReactElement;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createElement, Fragment, useCallback, useEffect, useRef, useSyncExternalStore, } from "react";
|
|
1
|
+
import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useSyncExternalStore, } from "react";
|
|
2
|
+
import { NativeViewPresentationSurface } from "./native-view-presentation-surface.js";
|
|
2
3
|
import { ViewWebViewHost } from "./view-webview-host.js";
|
|
3
|
-
function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, webViewComponent, webViewProps, }) {
|
|
4
|
+
function NativeViewHostProjectionEntry({ controller, nativeViewComponent, onInvalidMessage, record, webViewComponent, webViewProps, }) {
|
|
4
5
|
const detachConnectionRef = useRef(null);
|
|
5
6
|
const detachConnection = useCallback(() => {
|
|
6
7
|
const detach = detachConnectionRef.current;
|
|
@@ -8,15 +9,17 @@ function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, w
|
|
|
8
9
|
detach === null || detach === void 0 ? void 0 : detach();
|
|
9
10
|
}, []);
|
|
10
11
|
const handleConnected = useCallback((connection) => {
|
|
11
|
-
detachConnection();
|
|
12
12
|
const controllerDetach = controller.attachViewReadinessConnection({
|
|
13
13
|
allocationId: record.allocationId,
|
|
14
14
|
connection,
|
|
15
15
|
});
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (controllerDetach) {
|
|
17
|
+
detachConnectionRef.current = controllerDetach;
|
|
18
|
+
}
|
|
19
|
+
}, [controller, record.allocationId]);
|
|
20
|
+
const presentedWebViewProps = useMemo(() => (Object.assign(Object.assign({}, webViewProps), { style: [webViewProps === null || webViewProps === void 0 ? void 0 : webViewProps.style, { flex: 1 }] })), [webViewProps]);
|
|
18
21
|
useEffect(() => detachConnection, [detachConnection]);
|
|
19
|
-
return createElement(ViewWebViewHost, {
|
|
22
|
+
return createElement(NativeViewPresentationSurface, { nativeViewComponent, record }, createElement(ViewWebViewHost, {
|
|
20
23
|
generation: record.generation,
|
|
21
24
|
onConnected: handleConnected,
|
|
22
25
|
onDisconnected: detachConnection,
|
|
@@ -24,13 +27,14 @@ function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, w
|
|
|
24
27
|
srcdoc: record.srcdoc,
|
|
25
28
|
viewId: record.viewId,
|
|
26
29
|
webViewComponent,
|
|
27
|
-
webViewProps,
|
|
28
|
-
});
|
|
30
|
+
webViewProps: presentedWebViewProps,
|
|
31
|
+
}));
|
|
29
32
|
}
|
|
30
|
-
export function NativeViewHostProjection({ controller, onInvalidMessage, webViewComponent, webViewProps, }) {
|
|
33
|
+
export function NativeViewHostProjection({ controller, nativeViewComponent, onInvalidMessage, webViewComponent, webViewProps, }) {
|
|
31
34
|
const records = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
|
|
32
35
|
return createElement(Fragment, null, records.map((record) => createElement(NativeViewHostProjectionEntry, {
|
|
33
36
|
controller,
|
|
37
|
+
nativeViewComponent,
|
|
34
38
|
onInvalidMessage,
|
|
35
39
|
record,
|
|
36
40
|
webViewComponent,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ComponentType, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import type { NativeViewRecord } from "./native-view-record-controller.js";
|
|
3
|
+
export declare function NativeViewPresentationSurface({ children, nativeViewComponent, record, }: {
|
|
4
|
+
children?: ReactNode;
|
|
5
|
+
nativeViewComponent?: ComponentType<Record<string, unknown>>;
|
|
6
|
+
record: NativeViewRecord;
|
|
7
|
+
}): ReactElement;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createElement, } from "react";
|
|
2
|
+
import { toNativeWidgetCornerRadii } from "./native-widget-corner-radii.js";
|
|
3
|
+
import { toNativeWidgetResolvedBorderStyle } from "./native-widget-frame-border.js";
|
|
4
|
+
const HOST_SURFACE_STYLE = {
|
|
5
|
+
alignItems: "center",
|
|
6
|
+
bottom: 0,
|
|
7
|
+
justifyContent: "flex-end",
|
|
8
|
+
left: 0,
|
|
9
|
+
position: "absolute",
|
|
10
|
+
right: 0,
|
|
11
|
+
top: 0,
|
|
12
|
+
};
|
|
13
|
+
const HIDDEN_HOST_SURFACE_STYLE = {
|
|
14
|
+
opacity: 0,
|
|
15
|
+
};
|
|
16
|
+
const PRESENTED_HOST_SURFACE_STYLE = {
|
|
17
|
+
zIndex: 2147483647,
|
|
18
|
+
};
|
|
19
|
+
const TOP_PLACEMENT_STYLE = {
|
|
20
|
+
justifyContent: "flex-start",
|
|
21
|
+
};
|
|
22
|
+
const BOTTOM_PLACEMENT_STYLE = {
|
|
23
|
+
justifyContent: "flex-end",
|
|
24
|
+
};
|
|
25
|
+
const PREPARING_VIEW_FRAME_STYLE = {
|
|
26
|
+
height: "100%",
|
|
27
|
+
width: "100%",
|
|
28
|
+
};
|
|
29
|
+
const PRESENTED_VIEW_FRAME_STYLE = {
|
|
30
|
+
maxHeight: "100%",
|
|
31
|
+
maxWidth: "100%",
|
|
32
|
+
overflow: "hidden",
|
|
33
|
+
};
|
|
34
|
+
const VIEW_FRAME_BORDER_OVERLAY_STYLE = {
|
|
35
|
+
bottom: 0,
|
|
36
|
+
left: 0,
|
|
37
|
+
position: "absolute",
|
|
38
|
+
right: 0,
|
|
39
|
+
top: 0,
|
|
40
|
+
};
|
|
41
|
+
const resolveNativeView = () => {
|
|
42
|
+
const reactNativeModule = require("react-native");
|
|
43
|
+
if (!reactNativeModule.View) {
|
|
44
|
+
throw new Error("react-native must export View to present native views");
|
|
45
|
+
}
|
|
46
|
+
return reactNativeModule.View;
|
|
47
|
+
};
|
|
48
|
+
// TODO(migration): Project shadow appearance onto Core-owned native frames before layering and scoped dismissal.
|
|
49
|
+
// [from=native-frame-border-shadow-projection] [to=native-frame-shadow-projection] [scope=slice] [principle=core-owned-flow-lifecycle] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=189]
|
|
50
|
+
export function NativeViewPresentationSurface({ children, nativeViewComponent, record, }) {
|
|
51
|
+
var _a, _b, _c, _d, _e, _f;
|
|
52
|
+
const NativeView = nativeViewComponent !== null && nativeViewComponent !== void 0 ? nativeViewComponent : resolveNativeView();
|
|
53
|
+
const isPresented = record.activationState === "active" && record.size !== null;
|
|
54
|
+
const cornerRadii = toNativeWidgetCornerRadii((_a = record.size) === null || _a === void 0 ? void 0 : _a.frameAppearance);
|
|
55
|
+
const borderStyle = toNativeWidgetResolvedBorderStyle(((_c = (_b = record.size) === null || _b === void 0 ? void 0 : _b.frameAppearance) === null || _c === void 0 ? void 0 : _c.kind) === "page"
|
|
56
|
+
? record.size.frameAppearance.resolvedBorder
|
|
57
|
+
: undefined);
|
|
58
|
+
const cornerRadiusStyle = {
|
|
59
|
+
borderBottomLeftRadius: cornerRadii.bottomLeft,
|
|
60
|
+
borderBottomRightRadius: cornerRadii.bottomRight,
|
|
61
|
+
borderTopLeftRadius: cornerRadii.topLeft,
|
|
62
|
+
borderTopRightRadius: cornerRadii.topRight,
|
|
63
|
+
};
|
|
64
|
+
return createElement(NativeView, {
|
|
65
|
+
pointerEvents: isPresented ? "box-none" : "none",
|
|
66
|
+
style: Object.assign(Object.assign(Object.assign({}, HOST_SURFACE_STYLE), (((_d = record.size) === null || _d === void 0 ? void 0 : _d.placement) === "docked-top"
|
|
67
|
+
? TOP_PLACEMENT_STYLE
|
|
68
|
+
: BOTTOM_PLACEMENT_STYLE)), (isPresented
|
|
69
|
+
? PRESENTED_HOST_SURFACE_STYLE
|
|
70
|
+
: HIDDEN_HOST_SURFACE_STYLE)),
|
|
71
|
+
testID: `gx-native-view-presentation-${record.viewId}`,
|
|
72
|
+
}, createElement(NativeView, {
|
|
73
|
+
pointerEvents: isPresented ? "auto" : "none",
|
|
74
|
+
style: isPresented
|
|
75
|
+
? Object.assign(Object.assign(Object.assign({}, PRESENTED_VIEW_FRAME_STYLE), cornerRadiusStyle), { height: (_e = record.size) === null || _e === void 0 ? void 0 : _e.height, width: (_f = record.size) === null || _f === void 0 ? void 0 : _f.width }) : PREPARING_VIEW_FRAME_STYLE,
|
|
76
|
+
testID: `gx-native-view-frame-${record.viewId}`,
|
|
77
|
+
}, children, createElement(NativeView, {
|
|
78
|
+
pointerEvents: "none",
|
|
79
|
+
style: Object.assign(Object.assign(Object.assign({}, VIEW_FRAME_BORDER_OVERLAY_STYLE), cornerRadiusStyle), borderStyle),
|
|
80
|
+
testID: `gx-native-view-frame-border-${record.viewId}`,
|
|
81
|
+
})));
|
|
82
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OpenRequestMetadata } from "@getuserfeedback/protocol/internal/core-view-host-actions";
|
|
1
2
|
import { type EventViewSize } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
2
3
|
import type { CoreWebViewHostConnection } from "./core-webview-host-controller.js";
|
|
3
4
|
import type { ViewWebViewHostConnection } from "./view-webview-host-controller.js";
|
|
@@ -8,6 +9,7 @@ export type NativeViewRecord = Readonly<{
|
|
|
8
9
|
activationState: NativeViewActivationState;
|
|
9
10
|
generation: number;
|
|
10
11
|
instanceId: string;
|
|
12
|
+
openRequest: Readonly<OpenRequestMetadata> | undefined;
|
|
11
13
|
size: NativeViewSizeReport | null;
|
|
12
14
|
srcdoc: string;
|
|
13
15
|
viewId: string;
|
|
@@ -28,21 +28,26 @@ export function createNativeViewRecordController(input) {
|
|
|
28
28
|
listener();
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
const ensurePendingActivation = (viewId) => {
|
|
31
|
+
const ensurePendingActivation = (viewId, openRequest) => {
|
|
32
32
|
const existing = pendingActivations.get(viewId);
|
|
33
33
|
if (existing) {
|
|
34
|
+
existing.openRequest = openRequest;
|
|
34
35
|
return existing;
|
|
35
36
|
}
|
|
36
|
-
const created =
|
|
37
|
+
const created = {
|
|
38
|
+
activation: createViewActivationController(),
|
|
39
|
+
openRequest,
|
|
40
|
+
};
|
|
37
41
|
pendingActivations.set(viewId, created);
|
|
38
42
|
return created;
|
|
39
43
|
};
|
|
40
44
|
const removeEntry = (entry) => {
|
|
41
|
-
var _a;
|
|
42
45
|
if (entries.get(entry.viewId) !== entry) {
|
|
43
46
|
return false;
|
|
44
47
|
}
|
|
45
|
-
|
|
48
|
+
const detachViewReadiness = entry.viewReadinessDetach;
|
|
49
|
+
entry.viewReadinessDetach = null;
|
|
50
|
+
detachViewReadiness === null || detachViewReadiness === void 0 ? void 0 : detachViewReadiness();
|
|
46
51
|
entries.delete(entry.viewId);
|
|
47
52
|
if (!disposed) {
|
|
48
53
|
notify();
|
|
@@ -58,7 +63,8 @@ export function createNativeViewRecordController(input) {
|
|
|
58
63
|
if (!preparation) {
|
|
59
64
|
return;
|
|
60
65
|
}
|
|
61
|
-
const
|
|
66
|
+
const pendingActivation = pendingActivations.get(action.viewId);
|
|
67
|
+
const activation = (_a = pendingActivation === null || pendingActivation === void 0 ? void 0 : pendingActivation.activation) !== null && _a !== void 0 ? _a : createViewActivationController();
|
|
62
68
|
pendingActivations.delete(action.viewId);
|
|
63
69
|
activation.startPrerender();
|
|
64
70
|
nextAllocationId += 1;
|
|
@@ -67,6 +73,9 @@ export function createNativeViewRecordController(input) {
|
|
|
67
73
|
allocationId: nextAllocationId,
|
|
68
74
|
generation: action.gen,
|
|
69
75
|
instanceId: action.instanceId,
|
|
76
|
+
openRequest: pendingActivation
|
|
77
|
+
? pendingActivation.openRequest
|
|
78
|
+
: toOpenRequestMetadata(action.openRequest),
|
|
70
79
|
preparation,
|
|
71
80
|
preparationState: "claimed",
|
|
72
81
|
size: null,
|
|
@@ -81,12 +90,19 @@ export function createNativeViewRecordController(input) {
|
|
|
81
90
|
notify();
|
|
82
91
|
};
|
|
83
92
|
const handleActivate = (action) => {
|
|
84
|
-
var _a;
|
|
85
93
|
const entry = entries.get(action.viewId);
|
|
86
|
-
const
|
|
94
|
+
const openRequest = toOpenRequestMetadata(action.openRequest);
|
|
95
|
+
const activation = entry
|
|
96
|
+
? entry.activation
|
|
97
|
+
: ensurePendingActivation(action.viewId, openRequest).activation;
|
|
87
98
|
const previousState = activation.getState();
|
|
88
99
|
activation.requestActivation();
|
|
89
|
-
if (entry
|
|
100
|
+
if (!entry) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const openRequestChanged = !areOpenRequestsEqual(entry.openRequest, openRequest);
|
|
104
|
+
entry.openRequest = openRequest;
|
|
105
|
+
if (activation.getState() !== previousState || openRequestChanged) {
|
|
90
106
|
notify();
|
|
91
107
|
}
|
|
92
108
|
};
|
|
@@ -187,6 +203,12 @@ export function createNativeViewRecordController(input) {
|
|
|
187
203
|
connectionReadiness.release();
|
|
188
204
|
if (entry.viewReadinessDetach === detach) {
|
|
189
205
|
entry.viewReadinessDetach = null;
|
|
206
|
+
if (entry.size !== null) {
|
|
207
|
+
entry.size = null;
|
|
208
|
+
if (!disposed && entries.get(viewId) === entry) {
|
|
209
|
+
notify();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
190
212
|
}
|
|
191
213
|
};
|
|
192
214
|
const attemptReadinessDelivery = (isRetry = false) => {
|
|
@@ -281,6 +303,10 @@ export function createNativeViewRecordController(input) {
|
|
|
281
303
|
}
|
|
282
304
|
entry.viewReadinessDetach = detach;
|
|
283
305
|
previousDetach === null || previousDetach === void 0 ? void 0 : previousDetach();
|
|
306
|
+
if (entry.size !== null) {
|
|
307
|
+
entry.size = null;
|
|
308
|
+
notify();
|
|
309
|
+
}
|
|
284
310
|
subscribed = true;
|
|
285
311
|
for (const message of bufferedMessages) {
|
|
286
312
|
handleMessage(message);
|
|
@@ -332,11 +358,35 @@ function toRecord(entry) {
|
|
|
332
358
|
allocationId: entry.allocationId,
|
|
333
359
|
generation: entry.generation,
|
|
334
360
|
instanceId: entry.instanceId,
|
|
361
|
+
openRequest: entry.openRequest,
|
|
335
362
|
size: entry.size,
|
|
336
363
|
srcdoc: entry.srcdoc,
|
|
337
364
|
viewId: entry.viewId,
|
|
338
365
|
});
|
|
339
366
|
}
|
|
367
|
+
function toOpenRequestMetadata(openRequest) {
|
|
368
|
+
return openRequest === undefined
|
|
369
|
+
? undefined
|
|
370
|
+
: Object.freeze(Object.assign({}, openRequest));
|
|
371
|
+
}
|
|
372
|
+
function areOpenRequestsEqual(left, right) {
|
|
373
|
+
if (left === right) {
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
if (!left || !right || left.source !== right.source) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (left.source === "targeting" && right.source === "targeting") {
|
|
380
|
+
return left.flowId === right.flowId;
|
|
381
|
+
}
|
|
382
|
+
if (left.source !== "command" || right.source !== "command") {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
return (left.flowId === right.flowId &&
|
|
386
|
+
left.requestId === right.requestId &&
|
|
387
|
+
left.flowHandleId === right.flowHandleId &&
|
|
388
|
+
left.hideCloseButton === right.hideCloseButton);
|
|
389
|
+
}
|
|
340
390
|
function toViewSizeReport(event) {
|
|
341
391
|
return {
|
|
342
392
|
width: event.width,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { WidgetFrameAppearance, WidgetFrameResolvedBorderRadius } from "@getuserfeedback/protocol/host";
|
|
2
|
+
export type NativeWidgetCornerRadii = {
|
|
3
|
+
bottomLeft: number;
|
|
4
|
+
bottomRight: number;
|
|
5
|
+
topLeft: number;
|
|
6
|
+
topRight: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function safeParseNativeWidgetCanonicalRadii(value: string): NativeWidgetCornerRadii | undefined;
|
|
9
|
+
export declare function toNativeWidgetConservativeResolvedCornerRadii(resolved: WidgetFrameResolvedBorderRadius): NativeWidgetCornerRadii;
|
|
10
|
+
export declare function toNativeWidgetCornerRadii(appearance?: WidgetFrameAppearance): NativeWidgetCornerRadii;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const CLEARED_NATIVE_WIDGET_CORNER_RADII = {
|
|
2
|
+
bottomLeft: 0,
|
|
3
|
+
bottomRight: 0,
|
|
4
|
+
topLeft: 0,
|
|
5
|
+
topRight: 0,
|
|
6
|
+
};
|
|
7
|
+
const CANONICAL_PX_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d+)?px$/;
|
|
8
|
+
const safeParseCanonicalPx = (value) => {
|
|
9
|
+
if (!CANONICAL_PX_PATTERN.test(value)) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
const parsed = Number.parseFloat(value);
|
|
13
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
14
|
+
};
|
|
15
|
+
export function safeParseNativeWidgetCanonicalRadii(value) {
|
|
16
|
+
if (value.includes("/")) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const radii = value.trim().split(/\s+/).map(safeParseCanonicalPx);
|
|
20
|
+
if (radii.length === 0 ||
|
|
21
|
+
radii.length > 4 ||
|
|
22
|
+
radii.some((radius) => radius === undefined)) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const [topLeft, topRight = topLeft, bottomRight = topLeft, bottomLeft = topRight,] = radii;
|
|
26
|
+
if (topLeft === undefined ||
|
|
27
|
+
topRight === undefined ||
|
|
28
|
+
bottomRight === undefined ||
|
|
29
|
+
bottomLeft === undefined) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return { bottomLeft, bottomRight, topLeft, topRight };
|
|
33
|
+
}
|
|
34
|
+
export function toNativeWidgetConservativeResolvedCornerRadii(resolved) {
|
|
35
|
+
// React Native exposes one scalar per corner, so use a smaller circle that
|
|
36
|
+
// does not exceed the authored ellipse on either axis.
|
|
37
|
+
return {
|
|
38
|
+
bottomLeft: Math.min(resolved.bottomLeft.horizontalPx, resolved.bottomLeft.verticalPx),
|
|
39
|
+
bottomRight: Math.min(resolved.bottomRight.horizontalPx, resolved.bottomRight.verticalPx),
|
|
40
|
+
topLeft: Math.min(resolved.topLeft.horizontalPx, resolved.topLeft.verticalPx),
|
|
41
|
+
topRight: Math.min(resolved.topRight.horizontalPx, resolved.topRight.verticalPx),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function toNativeWidgetCornerRadii(appearance) {
|
|
45
|
+
var _a;
|
|
46
|
+
if (appearance === undefined || appearance.kind === "none") {
|
|
47
|
+
return Object.assign({}, CLEARED_NATIVE_WIDGET_CORNER_RADII);
|
|
48
|
+
}
|
|
49
|
+
const resolved = appearance.resolvedBorderRadius;
|
|
50
|
+
if (resolved !== undefined) {
|
|
51
|
+
return toNativeWidgetConservativeResolvedCornerRadii(resolved);
|
|
52
|
+
}
|
|
53
|
+
return ((_a = safeParseNativeWidgetCanonicalRadii(appearance.borderRadius)) !== null && _a !== void 0 ? _a : Object.assign({}, CLEARED_NATIVE_WIDGET_CORNER_RADII));
|
|
54
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WidgetFrameResolvedBorder } from "@getuserfeedback/protocol/host";
|
|
2
|
+
export type NativeWidgetFrameBorderStyle = {
|
|
3
|
+
borderColor: string;
|
|
4
|
+
borderStyle: "dashed" | "dotted" | "solid";
|
|
5
|
+
borderWidth: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function toNativeWidgetResolvedBorderStyle(border?: WidgetFrameResolvedBorder): NativeWidgetFrameBorderStyle;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const CLEARED_NATIVE_WIDGET_FRAME_BORDER_STYLE = {
|
|
2
|
+
borderColor: "transparent",
|
|
3
|
+
borderStyle: "solid",
|
|
4
|
+
borderWidth: 0,
|
|
5
|
+
};
|
|
6
|
+
export function toNativeWidgetResolvedBorderStyle(border) {
|
|
7
|
+
if (border === undefined ||
|
|
8
|
+
border.widthPx === 0 ||
|
|
9
|
+
border.style === "none" ||
|
|
10
|
+
border.style === "hidden") {
|
|
11
|
+
return Object.assign({}, CLEARED_NATIVE_WIDGET_FRAME_BORDER_STYLE);
|
|
12
|
+
}
|
|
13
|
+
const { alpha, blue, green, red } = border.color;
|
|
14
|
+
return {
|
|
15
|
+
borderColor: `rgba(${red}, ${green}, ${blue}, ${alpha})`,
|
|
16
|
+
borderStyle: border.style === "dashed" || border.style === "dotted"
|
|
17
|
+
? border.style
|
|
18
|
+
: "solid",
|
|
19
|
+
borderWidth: border.widthPx,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { WidgetFrameResolvedBoxShadowLayer } from "@getuserfeedback/protocol/host";
|
|
2
|
+
export type NativeWidgetFrameBoxShadowValue = {
|
|
3
|
+
blurRadius: number;
|
|
4
|
+
color: string;
|
|
5
|
+
inset: boolean;
|
|
6
|
+
offsetX: number;
|
|
7
|
+
offsetY: number;
|
|
8
|
+
spreadDistance: number;
|
|
9
|
+
};
|
|
10
|
+
type NativeWidgetFrameLegacyShadowStyle = {
|
|
11
|
+
elevation: number;
|
|
12
|
+
shadowColor: string;
|
|
13
|
+
shadowOffset: {
|
|
14
|
+
height: number;
|
|
15
|
+
width: number;
|
|
16
|
+
};
|
|
17
|
+
shadowOpacity: number;
|
|
18
|
+
shadowRadius: number;
|
|
19
|
+
};
|
|
20
|
+
export type NativeWidgetFrameShadowProjection = {
|
|
21
|
+
kind: "clear";
|
|
22
|
+
} | {
|
|
23
|
+
boxShadow: NativeWidgetFrameBoxShadowValue[];
|
|
24
|
+
fallbackStyle: NativeWidgetFrameLegacyShadowStyle;
|
|
25
|
+
kind: "resolved";
|
|
26
|
+
};
|
|
27
|
+
export type NativeWidgetBoxShadowSupport = "all" | "none" | "outset";
|
|
28
|
+
export declare function toNativeWidgetFrameShadow(layers?: WidgetFrameResolvedBoxShadowLayer[]): NativeWidgetFrameShadowProjection;
|
|
29
|
+
export declare function toNativeWidgetSupportedBoxShadow(shadow: NativeWidgetFrameShadowProjection, support?: NativeWidgetBoxShadowSupport): NativeWidgetFrameBoxShadowValue[] | undefined;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const CLEARED_NATIVE_WIDGET_FRAME_SHADOW_STYLE = {
|
|
2
|
+
elevation: 0,
|
|
3
|
+
shadowColor: "transparent",
|
|
4
|
+
shadowOffset: { height: 0, width: 0 },
|
|
5
|
+
shadowOpacity: 0,
|
|
6
|
+
shadowRadius: 0,
|
|
7
|
+
};
|
|
8
|
+
const toNativeWidgetFrameColor = (color) => `rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha})`;
|
|
9
|
+
const toNativeWidgetFrameBoxShadow = (layers) => layers.map((layer) => ({
|
|
10
|
+
blurRadius: layer.blurRadiusPx,
|
|
11
|
+
color: toNativeWidgetFrameColor(layer.color),
|
|
12
|
+
inset: layer.inset,
|
|
13
|
+
offsetX: layer.offsetXPx,
|
|
14
|
+
offsetY: layer.offsetYPx,
|
|
15
|
+
spreadDistance: layer.spreadRadiusPx,
|
|
16
|
+
}));
|
|
17
|
+
const toNativeWidgetFrameLegacyShadow = (layers) => {
|
|
18
|
+
const layer = layers.find((candidate) => !candidate.inset && candidate.color.alpha > 0);
|
|
19
|
+
if (layer === undefined) {
|
|
20
|
+
return Object.assign({}, CLEARED_NATIVE_WIDGET_FRAME_SHADOW_STYLE);
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
elevation: Math.max(1, Math.ceil(layer.blurRadiusPx + Math.max(0, layer.spreadRadiusPx))),
|
|
24
|
+
shadowColor: `rgb(${layer.color.red}, ${layer.color.green}, ${layer.color.blue})`,
|
|
25
|
+
shadowOffset: { height: layer.offsetYPx, width: layer.offsetXPx },
|
|
26
|
+
shadowOpacity: layer.color.alpha,
|
|
27
|
+
shadowRadius: layer.blurRadiusPx,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export function toNativeWidgetFrameShadow(layers) {
|
|
31
|
+
if (layers === undefined) {
|
|
32
|
+
return { kind: "clear" };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
boxShadow: toNativeWidgetFrameBoxShadow(layers),
|
|
36
|
+
fallbackStyle: toNativeWidgetFrameLegacyShadow(layers),
|
|
37
|
+
kind: "resolved",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function toNativeWidgetSupportedBoxShadow(shadow, support) {
|
|
41
|
+
if (shadow.kind !== "resolved" ||
|
|
42
|
+
support === undefined ||
|
|
43
|
+
support === "none") {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return support === "all"
|
|
47
|
+
? shadow.boxShadow
|
|
48
|
+
: shadow.boxShadow.filter((layer) => !layer.inset);
|
|
49
|
+
}
|
|
@@ -1,26 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { safeParseNativeWidgetCanonicalRadii } from "./native-widget-corner-radii.js";
|
|
2
2
|
const CANONICAL_RADIUS_COMPONENT_PATTERN = /^((?:\d+(?:\.\d+)?|\.\d+))(px|%)$/;
|
|
3
|
-
const safeParseCanonicalPx = (value) => {
|
|
4
|
-
if (!CANONICAL_PX_PATTERN.test(value)) {
|
|
5
|
-
return undefined;
|
|
6
|
-
}
|
|
7
|
-
const parsed = Number.parseFloat(value);
|
|
8
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
9
|
-
};
|
|
10
3
|
export function safeParseProviderWidgetCanonicalTopRadii(value) {
|
|
11
|
-
if (value.
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
const radii = value.split(/\s+/).map(safeParseCanonicalPx);
|
|
15
|
-
if (radii.length === 0 ||
|
|
16
|
-
radii.length > 4 ||
|
|
17
|
-
radii.some((radius) => radius === undefined)) {
|
|
4
|
+
if (value !== value.trim()) {
|
|
18
5
|
return undefined;
|
|
19
6
|
}
|
|
20
|
-
const
|
|
21
|
-
return
|
|
7
|
+
const radii = safeParseNativeWidgetCanonicalRadii(value);
|
|
8
|
+
return radii === undefined
|
|
22
9
|
? undefined
|
|
23
|
-
: { topLeft, topRight };
|
|
10
|
+
: { topLeft: radii.topLeft, topRight: radii.topRight };
|
|
24
11
|
}
|
|
25
12
|
const parseCanonicalRadiusComponent = (value) => {
|
|
26
13
|
var _a;
|
|
@@ -1,44 +1,15 @@
|
|
|
1
|
-
import type { WidgetFrameAppearance
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
borderWidth: number;
|
|
6
|
-
};
|
|
7
|
-
type ProviderWidgetFrameBoxShadowValue = {
|
|
8
|
-
blurRadius: number;
|
|
9
|
-
color: string;
|
|
10
|
-
inset: boolean;
|
|
11
|
-
offsetX: number;
|
|
12
|
-
offsetY: number;
|
|
13
|
-
spreadDistance: number;
|
|
14
|
-
};
|
|
15
|
-
type ProviderWidgetFrameLegacyShadowStyle = {
|
|
16
|
-
elevation: number;
|
|
17
|
-
shadowColor: string;
|
|
18
|
-
shadowOffset: {
|
|
19
|
-
height: number;
|
|
20
|
-
width: number;
|
|
21
|
-
};
|
|
22
|
-
shadowOpacity: number;
|
|
23
|
-
shadowRadius: number;
|
|
24
|
-
};
|
|
25
|
-
export type ProviderWidgetFrameShadowProjection = {
|
|
26
|
-
kind: "clear";
|
|
27
|
-
} | {
|
|
1
|
+
import type { WidgetFrameAppearance } from "@getuserfeedback/protocol/host";
|
|
2
|
+
import { type NativeWidgetFrameBorderStyle } from "./native-widget-frame-border.js";
|
|
3
|
+
import { type NativeWidgetFrameShadowProjection } from "./native-widget-frame-shadow.js";
|
|
4
|
+
type ProviderWidgetFrameShadowProjection = NativeWidgetFrameShadowProjection | {
|
|
28
5
|
kind: "legacy";
|
|
29
|
-
} | {
|
|
30
|
-
boxShadow: ProviderWidgetFrameBoxShadowValue[];
|
|
31
|
-
fallbackStyle: ProviderWidgetFrameLegacyShadowStyle;
|
|
32
|
-
kind: "resolved";
|
|
33
6
|
};
|
|
34
7
|
export type ProviderWidgetFrameAppearanceProjection = {
|
|
35
|
-
borderStyle?:
|
|
8
|
+
borderStyle?: NativeWidgetFrameBorderStyle;
|
|
36
9
|
shadow: ProviderWidgetFrameShadowProjection;
|
|
37
10
|
topLeftRadius: number;
|
|
38
11
|
topRightRadius: number;
|
|
39
12
|
};
|
|
40
|
-
export declare function toProviderWidgetFrameShadow(layers?: WidgetFrameResolvedBoxShadowLayer[]): ProviderWidgetFrameShadowProjection;
|
|
41
|
-
export declare function toProviderWidgetFrameBorderStyle(border?: WidgetFrameResolvedBorder): ProviderWidgetFrameBorderStyle;
|
|
42
13
|
export declare function toProviderWidgetFrameAppearance(appearance?: WidgetFrameAppearance, nativeSheetSize?: {
|
|
43
14
|
height: number;
|
|
44
15
|
width: number;
|
|
@@ -1,71 +1,12 @@
|
|
|
1
|
+
import { toNativeWidgetConservativeResolvedCornerRadii } from "./native-widget-corner-radii.js";
|
|
2
|
+
import { toNativeWidgetResolvedBorderStyle, } from "./native-widget-frame-border.js";
|
|
3
|
+
import { toNativeWidgetFrameShadow, } from "./native-widget-frame-shadow.js";
|
|
1
4
|
import { resolveProviderWidgetCanonicalBorderRadius, safeParseProviderWidgetCanonicalTopRadii, } from "./provider-widget-border-radius.js";
|
|
2
5
|
const LEGACY_WIDGET_SHEET_RADIUS = 24;
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
borderWidth: 0,
|
|
6
|
+
const toProviderWidgetConservativeResolvedTopRadii = (radius) => {
|
|
7
|
+
const resolved = toNativeWidgetConservativeResolvedCornerRadii(radius);
|
|
8
|
+
return { topLeft: resolved.topLeft, topRight: resolved.topRight };
|
|
7
9
|
};
|
|
8
|
-
const CLEARED_WIDGET_FRAME_SHADOW_STYLE = {
|
|
9
|
-
elevation: 0,
|
|
10
|
-
shadowColor: "transparent",
|
|
11
|
-
shadowOffset: { height: 0, width: 0 },
|
|
12
|
-
shadowOpacity: 0,
|
|
13
|
-
shadowRadius: 0,
|
|
14
|
-
};
|
|
15
|
-
const toProviderWidgetFrameColor = (color) => `rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha})`;
|
|
16
|
-
const toProviderWidgetFrameBoxShadow = (layers) => layers.map((layer) => ({
|
|
17
|
-
blurRadius: layer.blurRadiusPx,
|
|
18
|
-
color: toProviderWidgetFrameColor(layer.color),
|
|
19
|
-
inset: layer.inset,
|
|
20
|
-
offsetX: layer.offsetXPx,
|
|
21
|
-
offsetY: layer.offsetYPx,
|
|
22
|
-
spreadDistance: layer.spreadRadiusPx,
|
|
23
|
-
}));
|
|
24
|
-
const toProviderWidgetFrameLegacyShadow = (layers) => {
|
|
25
|
-
const layer = layers.find((candidate) => !candidate.inset && candidate.color.alpha > 0);
|
|
26
|
-
if (layer === undefined) {
|
|
27
|
-
return Object.assign({}, CLEARED_WIDGET_FRAME_SHADOW_STYLE);
|
|
28
|
-
}
|
|
29
|
-
return {
|
|
30
|
-
elevation: Math.max(1, Math.ceil(layer.blurRadiusPx + Math.max(0, layer.spreadRadiusPx))),
|
|
31
|
-
shadowColor: `rgb(${layer.color.red}, ${layer.color.green}, ${layer.color.blue})`,
|
|
32
|
-
shadowOffset: { height: layer.offsetYPx, width: layer.offsetXPx },
|
|
33
|
-
shadowOpacity: layer.color.alpha,
|
|
34
|
-
shadowRadius: layer.blurRadiusPx,
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
export function toProviderWidgetFrameShadow(layers) {
|
|
38
|
-
if (layers === undefined) {
|
|
39
|
-
return { kind: "clear" };
|
|
40
|
-
}
|
|
41
|
-
return {
|
|
42
|
-
boxShadow: toProviderWidgetFrameBoxShadow(layers),
|
|
43
|
-
fallbackStyle: toProviderWidgetFrameLegacyShadow(layers),
|
|
44
|
-
kind: "resolved",
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
const toProviderWidgetConservativeResolvedTopRadii = (radius) => ({
|
|
48
|
-
// React Native exposes one scalar per corner, so use a deliberately smaller
|
|
49
|
-
// circle that does not exceed the authored ellipse on either axis.
|
|
50
|
-
topLeft: Math.min(radius.topLeft.horizontalPx, radius.topLeft.verticalPx),
|
|
51
|
-
topRight: Math.min(radius.topRight.horizontalPx, radius.topRight.verticalPx),
|
|
52
|
-
});
|
|
53
|
-
export function toProviderWidgetFrameBorderStyle(border) {
|
|
54
|
-
if (border === undefined ||
|
|
55
|
-
border.widthPx === 0 ||
|
|
56
|
-
border.style === "none" ||
|
|
57
|
-
border.style === "hidden") {
|
|
58
|
-
return Object.assign({}, CLEARED_WIDGET_FRAME_BORDER_STYLE);
|
|
59
|
-
}
|
|
60
|
-
const { alpha, blue, green, red } = border.color;
|
|
61
|
-
return {
|
|
62
|
-
borderColor: `rgba(${red}, ${green}, ${blue}, ${alpha})`,
|
|
63
|
-
borderStyle: border.style === "dashed" || border.style === "dotted"
|
|
64
|
-
? border.style
|
|
65
|
-
: "solid",
|
|
66
|
-
borderWidth: border.widthPx,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
10
|
export function toProviderWidgetFrameAppearance(appearance, nativeSheetSize) {
|
|
70
11
|
var _a, _b;
|
|
71
12
|
if (appearance === undefined) {
|
|
@@ -77,8 +18,8 @@ export function toProviderWidgetFrameAppearance(appearance, nativeSheetSize) {
|
|
|
77
18
|
}
|
|
78
19
|
if (appearance.kind === "none") {
|
|
79
20
|
return {
|
|
80
|
-
borderStyle:
|
|
81
|
-
shadow:
|
|
21
|
+
borderStyle: toNativeWidgetResolvedBorderStyle(),
|
|
22
|
+
shadow: toNativeWidgetFrameShadow(),
|
|
82
23
|
topLeftRadius: 0,
|
|
83
24
|
topRightRadius: 0,
|
|
84
25
|
};
|
|
@@ -94,8 +35,8 @@ export function toProviderWidgetFrameAppearance(appearance, nativeSheetSize) {
|
|
|
94
35
|
? safeParseProviderWidgetCanonicalTopRadii(appearance.borderRadius)
|
|
95
36
|
: toProviderWidgetConservativeResolvedTopRadii(appearance.resolvedBorderRadius);
|
|
96
37
|
return {
|
|
97
|
-
borderStyle:
|
|
98
|
-
shadow:
|
|
38
|
+
borderStyle: toNativeWidgetResolvedBorderStyle(appearance.resolvedBorder),
|
|
39
|
+
shadow: toNativeWidgetFrameShadow(appearance.resolvedBoxShadow),
|
|
99
40
|
topLeftRadius: (_a = radius === null || radius === void 0 ? void 0 : radius.topLeft) !== null && _a !== void 0 ? _a : 0,
|
|
100
41
|
topRightRadius: (_b = radius === null || radius === void 0 ? void 0 : radius.topRight) !== null && _b !== void 0 ? _b : 0,
|
|
101
42
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { resolveWidgetLayoutTransitionExecution, type WidgetFrameAppearance, type WidgetLayoutComparableSizeUpdate, type WidgetLayoutSizeUpdate, type WidgetLayoutTimedCapableTransitionPlan, type WidgetLayoutTransitionPolicy, type WidgetLayoutTransitionSchedule } from "@getuserfeedback/protocol/host";
|
|
2
2
|
import { type ComponentType, type ReactElement, type ReactNode } from "react";
|
|
3
|
+
import { type NativeWidgetBoxShadowSupport } from "./native-widget-frame-shadow.js";
|
|
3
4
|
import type { WidgetHostProps } from "./widget-host.js";
|
|
4
5
|
type NativeViewComponent = ComponentType<Record<string, unknown>>;
|
|
5
6
|
type NativeReactVersion = {
|
|
6
7
|
major?: unknown;
|
|
7
8
|
minor?: unknown;
|
|
8
9
|
};
|
|
9
|
-
export type ProviderWidgetNativeBoxShadowSupport =
|
|
10
|
+
export type ProviderWidgetNativeBoxShadowSupport = NativeWidgetBoxShadowSupport;
|
|
10
11
|
export declare function toProviderWidgetResumedTransitionTiming(input: {
|
|
11
12
|
durationMs: number;
|
|
12
13
|
easing: readonly [number, number, number, number];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { areWidgetFrameAppearancesEqual, planWidgetLayoutTransition, resolveWidgetLayoutResumedEasing, resolveWidgetLayoutTransitionExecution, toInstantWidgetLayoutSizeUpdate, } from "@getuserfeedback/protocol/host";
|
|
2
2
|
import { createElement, useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
+
import { toNativeWidgetSupportedBoxShadow, } from "./native-widget-frame-shadow.js";
|
|
3
4
|
import { toProviderWidgetFrameAppearance } from "./provider-widget-frame-appearance.js";
|
|
4
5
|
const HIDDEN_WIDGET_HOST_STYLE = {
|
|
5
6
|
bottom: 0,
|
|
@@ -192,6 +193,10 @@ function resolveNativeBoxShadowSupport() {
|
|
|
192
193
|
const reactNativeModule = require("react-native");
|
|
193
194
|
// React Native exposes renderer capability to JavaScript process-wide.
|
|
194
195
|
// There is no stable public API for querying the renderer of this View.
|
|
196
|
+
// TODO(architecture): Extract native box-shadow capability detection to
|
|
197
|
+
// native-widget-frame-shadow-support once the overlay diff fits the small-PR
|
|
198
|
+
// production line gate. [principle=architecture-gardening] [scope=slice]
|
|
199
|
+
// [priority=medium]
|
|
195
200
|
const fabricUIManager = Reflect.get(globalThis, "nativeFabricUIManager");
|
|
196
201
|
return toProviderWidgetNativeBoxShadowSupport({
|
|
197
202
|
hasFabricUIManager: fabricUIManager !== undefined && fabricUIManager !== null,
|
|
@@ -289,13 +294,9 @@ export function toProviderWidgetSheetSurfaceStyles(input) {
|
|
|
289
294
|
borderTopLeftRadius: appearance.topLeftRadius,
|
|
290
295
|
borderTopRightRadius: appearance.topRightRadius,
|
|
291
296
|
};
|
|
292
|
-
const nativeBoxShadow = appearance.shadow.kind
|
|
293
|
-
input.nativeBoxShadowSupport === undefined ||
|
|
294
|
-
input.nativeBoxShadowSupport === "none"
|
|
297
|
+
const nativeBoxShadow = appearance.shadow.kind === "legacy"
|
|
295
298
|
? undefined
|
|
296
|
-
: input.nativeBoxShadowSupport
|
|
297
|
-
? appearance.shadow.boxShadow
|
|
298
|
-
: appearance.shadow.boxShadow.filter((shadow) => !shadow.inset);
|
|
299
|
+
: toNativeWidgetSupportedBoxShadow(appearance.shadow, input.nativeBoxShadowSupport);
|
|
299
300
|
const usesNativeBoxShadow = nativeBoxShadow !== undefined;
|
|
300
301
|
const sheetShadowStyle = appearance.shadow.kind === "legacy"
|
|
301
302
|
? WIDGET_SHEET_LEGACY_FRAME_STYLE
|
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 : "3.0.
|
|
6
|
+
export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.90";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react-native",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.90",
|
|
4
4
|
"description": "getuserfeedback React Native SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"lint": "biome check ."
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@getuserfeedback/protocol": "^3.
|
|
48
|
-
"@getuserfeedback/sdk": "^0.12.
|
|
47
|
+
"@getuserfeedback/protocol": "^3.21.0",
|
|
48
|
+
"@getuserfeedback/sdk": "^0.12.6",
|
|
49
49
|
"robot3": "^1.2.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|