@getuserfeedback/react-native 1.0.0 → 1.3.3

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 CHANGED
@@ -1,14 +1,819 @@
1
- import { createContext, createElement, useContext, useMemo, } from "react";
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { assertNoSegmentExternalIdsInValueArgument, CommandSettlementError, CommandSettlementTimeoutError, } from "@getuserfeedback/protocol/host";
13
+ import { parseWebViewTransportNativeMessage } from "@getuserfeedback/protocol/webview-transport";
14
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
15
+ import { useResolvedHostViewport } from "./host-viewport.js";
16
+ import { applyProviderOverlayFlowState, applyProviderOverlayHandleInvalidated, createDefaultProviderOverlayState, DEFAULT_PROVIDER_OVERLAY_VISIBILITY, updateProviderOverlayReadySequence, } from "./provider-overlay-state.js";
17
+ import { REACT_NATIVE_SDK_VERSION } from "./version.js";
18
+ import { WidgetHost, } from "./widget-host.js";
19
+ export { REACT_NATIVE_SDK_VERSION } from "./version.js";
20
+ export { buildNativeMessageInjectionScript, buildWebViewBridgeScript, } from "./webview-bridge-script.js";
21
+ export { WidgetHost };
2
22
  const GetUserFeedbackNativeContext = createContext(null);
3
- export function GetUserFeedbackProvider({ children, configureOptions, initOptions, onWebMessage, }) {
4
- const contextValue = useMemo(() => ({
5
- configureOptions,
6
- initOptions,
23
+ const DEFAULT_COMMAND_TIMEOUT_MS = 30000;
24
+ const PROVIDER_UNMOUNTED_ERROR_MESSAGE = "GetUserFeedbackProvider unmounted before command settled";
25
+ const STARTUP_COMMAND_REPLACED_ERROR_MESSAGE = "GetUserFeedbackProvider replaced startup command before settlement";
26
+ const CLIENT_META = {
27
+ clientName: "@getuserfeedback/react-native",
28
+ clientVersion: REACT_NATIVE_SDK_VERSION,
29
+ loader: "sdk",
30
+ transport: "loader",
31
+ };
32
+ const HIDDEN_WIDGET_HOST_STYLE = {
33
+ height: 1,
34
+ left: -1,
35
+ opacity: 0,
36
+ position: "absolute",
37
+ top: -1,
38
+ width: 1,
39
+ };
40
+ const WIDGET_SHEET_OVERLAY_STYLE = {
41
+ bottom: 0,
42
+ left: 0,
43
+ position: "absolute",
44
+ right: 0,
45
+ top: 0,
46
+ zIndex: 2147483647,
47
+ };
48
+ const WIDGET_SHEET_BACKDROP_STYLE = {
49
+ backgroundColor: "rgba(15, 23, 42, 0.36)",
50
+ bottom: 0,
51
+ left: 0,
52
+ position: "absolute",
53
+ right: 0,
54
+ top: 0,
55
+ };
56
+ const WIDGET_SHEET_STYLE = {
57
+ backgroundColor: "#ffffff",
58
+ borderTopLeftRadius: 24,
59
+ borderTopRightRadius: 24,
60
+ bottom: 0,
61
+ left: 0,
62
+ overflow: "hidden",
63
+ position: "absolute",
64
+ right: 0,
65
+ shadowColor: "#000000",
66
+ shadowOffset: { height: -4, width: 0 },
67
+ shadowOpacity: 0.16,
68
+ shadowRadius: 18,
69
+ elevation: 18,
70
+ };
71
+ const VISIBLE_WIDGET_HOST_STYLE = {
72
+ flex: 1,
73
+ };
74
+ const VISIBLE_WIDGET_HOST_LAYOUT_STYLE_KEYS = new Set([
75
+ "alignSelf",
76
+ "bottom",
77
+ "display",
78
+ "end",
79
+ "flex",
80
+ "flexBasis",
81
+ "flexGrow",
82
+ "flexShrink",
83
+ "height",
84
+ "left",
85
+ "margin",
86
+ "marginBottom",
87
+ "marginEnd",
88
+ "marginHorizontal",
89
+ "marginLeft",
90
+ "marginRight",
91
+ "marginStart",
92
+ "marginTop",
93
+ "marginVertical",
94
+ "maxHeight",
95
+ "maxWidth",
96
+ "minHeight",
97
+ "minWidth",
98
+ "position",
99
+ "right",
100
+ "start",
101
+ "top",
102
+ "width",
103
+ ]);
104
+ const DEFAULT_WIDGET_SHEET_HEIGHT_RATIO = 0.86;
105
+ const DEFAULT_WIDGET_SHEET_MIN_HEIGHT = 320;
106
+ const createUniqueId = (prefix) => {
107
+ if (typeof globalThis.crypto !== "undefined" &&
108
+ typeof globalThis.crypto.randomUUID === "function") {
109
+ return `${prefix}_${globalThis.crypto.randomUUID()}`;
110
+ }
111
+ return `${prefix}_${Date.now()}_${Math.random().toString(16).slice(2)}`;
112
+ };
113
+ const createCommandRequestId = () => createUniqueId("req");
114
+ const createTransportMessageId = () => createUniqueId("native");
115
+ const normalizeOptionalString = (value) => {
116
+ if (value === undefined) {
117
+ return undefined;
118
+ }
119
+ const normalized = value.trim();
120
+ return normalized.length > 0 ? normalized : undefined;
121
+ };
122
+ const resolveRequiredString = (name, value) => {
123
+ const normalized = normalizeOptionalString(value);
124
+ if (normalized === undefined) {
125
+ throw new Error(`${name} must be a non-empty string`);
126
+ }
127
+ return normalized;
128
+ };
129
+ const toJsonKey = (value) => JSON.stringify(value);
130
+ const toStableHash = (value) => {
131
+ const json = toJsonKey(value);
132
+ let hash = 0x811c9dc5;
133
+ for (let index = 0; index < json.length; index += 1) {
134
+ hash ^= json.charCodeAt(index);
135
+ hash = Math.imul(hash, 0x01000193);
136
+ }
137
+ return (hash >>> 0).toString(16);
138
+ };
139
+ const FallbackNativeView = (_a) => {
140
+ var { children } = _a, props = __rest(_a, ["children"]);
141
+ return createElement("div", props, children);
142
+ };
143
+ function resolveNativeView() {
144
+ var _a;
145
+ try {
146
+ const reactNativeModule = require("react-native");
147
+ return (_a = reactNativeModule.View) !== null && _a !== void 0 ? _a : FallbackNativeView;
148
+ }
149
+ catch (_b) {
150
+ return FallbackNativeView;
151
+ }
152
+ }
153
+ function toSheetHostViewport(viewport) {
154
+ if (!viewport) {
155
+ return undefined;
156
+ }
157
+ const desiredHeight = Math.max(DEFAULT_WIDGET_SHEET_MIN_HEIGHT, Math.round(viewport.height * DEFAULT_WIDGET_SHEET_HEIGHT_RATIO));
158
+ return {
159
+ width: viewport.width,
160
+ height: Math.min(viewport.height, desiredHeight),
161
+ };
162
+ }
163
+ function toVisibleWidgetHostPresentationStyle(style) {
164
+ if (style === undefined || style === null) {
165
+ return undefined;
166
+ }
167
+ if (Array.isArray(style)) {
168
+ const styles = style
169
+ .map(toVisibleWidgetHostPresentationStyle)
170
+ .filter((entry) => entry !== undefined && entry !== null);
171
+ return styles.length > 0 ? styles : undefined;
172
+ }
173
+ if (typeof style !== "object") {
174
+ return style;
175
+ }
176
+ const presentationStyle = {};
177
+ for (const [key, value] of Object.entries(style)) {
178
+ if (!VISIBLE_WIDGET_HOST_LAYOUT_STYLE_KEYS.has(key)) {
179
+ presentationStyle[key] = value;
180
+ }
181
+ }
182
+ return Object.keys(presentationStyle).length > 0
183
+ ? presentationStyle
184
+ : undefined;
185
+ }
186
+ const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
187
+ if (typeof identifyInput === "string") {
188
+ assertNoSegmentExternalIdsInValueArgument({
189
+ argument: traitsOrOptions,
190
+ commandName: "identify",
191
+ valueArgumentName: "traits",
192
+ callShape: "identify(userId, traits, { externalIds })",
193
+ });
194
+ return {
195
+ kind: "identify",
196
+ userId: resolveRequiredString("userId", identifyInput),
197
+ traits: traitsOrOptions,
198
+ options,
199
+ };
200
+ }
201
+ assertNoSegmentExternalIdsInValueArgument({
202
+ argument: identifyInput,
203
+ commandName: "identify",
204
+ valueArgumentName: "traits",
205
+ callShape: "identify(traits, { externalIds })",
206
+ });
207
+ return {
208
+ kind: "identify",
209
+ traits: identifyInput,
210
+ options: options !== null && options !== void 0 ? options : traitsOrOptions,
211
+ };
212
+ };
213
+ const isFlowCommand = (command) => command.kind === "open" ||
214
+ command.kind === "prefetch" ||
215
+ command.kind === "prerender";
216
+ const toSettledFlowHandleId = (result) => {
217
+ if (typeof result !== "object" || result === null) {
218
+ return undefined;
219
+ }
220
+ const flowHandleId = result.flowHandleId;
221
+ return typeof flowHandleId === "string" && flowHandleId.trim().length > 0
222
+ ? flowHandleId
223
+ : undefined;
224
+ };
225
+ const toCommandFlowId = (command) => {
226
+ if (!isFlowCommand(command)) {
227
+ return undefined;
228
+ }
229
+ return command.flowId;
230
+ };
231
+ const toRetryWithoutFlowHandle = (command) => {
232
+ if (!isFlowCommand(command) || command.flowHandleId === undefined) {
233
+ return null;
234
+ }
235
+ return Object.assign(Object.assign({}, command), { flowHandleId: undefined });
236
+ };
237
+ const toCommandSettlementError = (event) => {
238
+ if (event.detail.ok) {
239
+ return null;
240
+ }
241
+ return new CommandSettlementError({
242
+ code: event.detail.error.code,
243
+ instanceId: event.detail.instanceId,
244
+ kind: event.detail.kind,
245
+ message: event.detail.error.message,
246
+ requestId: event.detail.requestId,
247
+ });
248
+ };
249
+ export function GetUserFeedbackProvider({ autoInit = true, children, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, configureOptions, hostViewport, instanceId: instanceIdProp, initOptions, loaderUrl, onCommandError, onInvalidWebMessage, onWebMessage, source, webViewComponent, webViewProps, }) {
250
+ var _a, _b, _c;
251
+ const NativeView = useMemo(resolveNativeView, []);
252
+ const [instanceId] = useState(() => { var _a; return (_a = normalizeOptionalString(instanceIdProp)) !== null && _a !== void 0 ? _a : createUniqueId("rn"); });
253
+ const [nativeMessages, setNativeMessages] = useState([]);
254
+ const [widgetHostVisibility, setWidgetHostVisibility] = useState(DEFAULT_PROVIDER_OVERLAY_VISIBILITY);
255
+ const resolvedHostViewport = useResolvedHostViewport(hostViewport);
256
+ const nativeMessagesRef = useRef([]);
257
+ const pendingCommandsRef = useRef(new Map());
258
+ const hostEpochRef = useRef(0);
259
+ const flowHandleCacheEpochRef = useRef(0);
260
+ const flowHandleIdsByFlowIdRef = useRef(new Map());
261
+ const flowCommandSequencesByFlowIdRef = useRef(new Map());
262
+ const providerOverlayStateRef = useRef(createDefaultProviderOverlayState());
263
+ const initOptionsRef = useRef(initOptions);
264
+ const configureOptionsRef = useRef(configureOptions);
265
+ const initOptionsKey = useMemo(() => toJsonKey(initOptions), [initOptions]);
266
+ const configureOptionsKey = useMemo(() => (configureOptions === undefined ? null : toJsonKey(configureOptions)), [configureOptions]);
267
+ const hostSourceKey = useMemo(() => toJsonKey({ loaderUrl: loaderUrl !== null && loaderUrl !== void 0 ? loaderUrl : null, source: source !== null && source !== void 0 ? source : null }), [loaderUrl, source]);
268
+ const startupOptionsKey = useMemo(() => toJsonKey({ configureOptionsKey, initOptionsKey }), [configureOptionsKey, initOptionsKey]);
269
+ const startupIdempotencyKey = useMemo(() => toStableHash({ configureOptionsKey, initOptionsKey }), [configureOptionsKey, initOptionsKey]);
270
+ const startupQueuedKeyRef = useRef(null);
271
+ const lastReadyRef = useRef(null);
272
+ const visibilitySourceKeyRef = useRef(hostSourceKey);
273
+ const hostSourceKeyRef = useRef(hostSourceKey);
274
+ const hostSourceVersionRef = useRef(0);
275
+ if (hostSourceKeyRef.current !== hostSourceKey) {
276
+ hostSourceKeyRef.current = hostSourceKey;
277
+ hostSourceVersionRef.current += 1;
278
+ for (const pendingCommand of pendingCommandsRef.current.values()) {
279
+ if (!pendingCommand.timeout) {
280
+ continue;
281
+ }
282
+ clearTimeout(pendingCommand.timeout);
283
+ pendingCommand.timeout = null;
284
+ }
285
+ lastReadyRef.current = null;
286
+ startupQueuedKeyRef.current = null;
287
+ flowHandleCacheEpochRef.current += 1;
288
+ flowHandleIdsByFlowIdRef.current.clear();
289
+ providerOverlayStateRef.current = createDefaultProviderOverlayState();
290
+ }
291
+ initOptionsRef.current = initOptions;
292
+ configureOptionsRef.current = configureOptions;
293
+ nativeMessagesRef.current = nativeMessages;
294
+ const reportCommandError = useCallback((error) => {
295
+ if (error.message === PROVIDER_UNMOUNTED_ERROR_MESSAGE ||
296
+ error.message === STARTUP_COMMAND_REPLACED_ERROR_MESSAGE) {
297
+ return;
298
+ }
299
+ if (onCommandError) {
300
+ onCommandError(error);
301
+ return;
302
+ }
303
+ setTimeout(() => {
304
+ throw error;
305
+ }, 0);
306
+ }, [onCommandError]);
307
+ const rejectPendingCommand = useCallback((requestId, pendingCommand, error) => {
308
+ pendingCommandsRef.current.delete(requestId);
309
+ if (pendingCommand.timeout) {
310
+ clearTimeout(pendingCommand.timeout);
311
+ pendingCommand.timeout = null;
312
+ }
313
+ if (pendingCommand.isStartup) {
314
+ startupQueuedKeyRef.current = null;
315
+ }
316
+ setNativeMessages((messages) => messages.filter((entry) => entry.id !== pendingCommand.messageId));
317
+ pendingCommand.reject(error);
318
+ }, []);
319
+ const startPendingCommandTimeout = useCallback((requestId, pendingCommand) => {
320
+ var _a, _b;
321
+ pendingCommand.hostEpoch = (_b = (_a = lastReadyRef.current) === null || _a === void 0 ? void 0 : _a.hostEpoch) !== null && _b !== void 0 ? _b : null;
322
+ if (commandTimeoutMs <= 0) {
323
+ return;
324
+ }
325
+ if (pendingCommand.timeout) {
326
+ clearTimeout(pendingCommand.timeout);
327
+ }
328
+ pendingCommand.timeout = setTimeout(() => {
329
+ rejectPendingCommand(requestId, pendingCommand, new CommandSettlementTimeoutError(requestId));
330
+ }, commandTimeoutMs);
331
+ }, [commandTimeoutMs, rejectPendingCommand]);
332
+ const startPendingCommandTimeouts = useCallback(() => {
333
+ for (const [requestId, pendingCommand] of pendingCommandsRef.current) {
334
+ startPendingCommandTimeout(requestId, pendingCommand);
335
+ }
336
+ }, [startPendingCommandTimeout]);
337
+ const pausePendingCommandTimeouts = useCallback(() => {
338
+ for (const pendingCommand of pendingCommandsRef.current.values()) {
339
+ if (!pendingCommand.timeout) {
340
+ continue;
341
+ }
342
+ clearTimeout(pendingCommand.timeout);
343
+ pendingCommand.timeout = null;
344
+ }
345
+ }, []);
346
+ const enqueueTransportCommand = useCallback((command, options, { isStartup = false, prioritizeStartup = false, } = {}) => {
347
+ var _a, _b;
348
+ const requestId = createCommandRequestId();
349
+ const idempotencyKey = (_a = normalizeOptionalString(options === null || options === void 0 ? void 0 : options.idempotencyKey)) !== null && _a !== void 0 ? _a : requestId;
350
+ const envelope = {
351
+ version: "1",
352
+ instanceId,
353
+ requestId,
354
+ idempotencyKey,
355
+ clientMeta: CLIENT_META,
356
+ command,
357
+ };
358
+ const message = {
359
+ kind: "enqueueCommand",
360
+ id: createTransportMessageId(),
361
+ envelope,
362
+ };
363
+ let validatedMessage;
364
+ try {
365
+ validatedMessage = parseWebViewTransportNativeMessage(message);
366
+ }
367
+ catch (error) {
368
+ if (isStartup) {
369
+ startupQueuedKeyRef.current = null;
370
+ }
371
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
372
+ }
373
+ const result = {
374
+ envelope: validatedMessage.envelope,
375
+ message: validatedMessage,
376
+ };
377
+ const flowId = toCommandFlowId(validatedMessage.envelope.command);
378
+ const flowCommandSequence = flowId === undefined
379
+ ? null
380
+ : ((_b = flowCommandSequencesByFlowIdRef.current.get(flowId)) !== null && _b !== void 0 ? _b : 0) + 1;
381
+ if (flowId !== undefined && flowCommandSequence !== null) {
382
+ flowCommandSequencesByFlowIdRef.current.set(flowId, flowCommandSequence);
383
+ }
384
+ const promise = new Promise((resolve, reject) => {
385
+ pendingCommandsRef.current.set(requestId, {
386
+ flowCommandSequence,
387
+ flowHandleCacheEpoch: flowHandleCacheEpochRef.current,
388
+ hostEpoch: null,
389
+ isStartup,
390
+ messageId: validatedMessage.id,
391
+ reject,
392
+ resolve,
393
+ result,
394
+ timeout: null,
395
+ });
396
+ if (lastReadyRef.current !== null) {
397
+ startPendingCommandTimeout(requestId, pendingCommandsRef.current.get(requestId));
398
+ }
399
+ });
400
+ setNativeMessages((messages) => {
401
+ if (!(isStartup && prioritizeStartup)) {
402
+ return [...messages, validatedMessage];
403
+ }
404
+ const firstActionIndex = messages.findIndex((entry) => {
405
+ const pendingCommand = pendingCommandsRef.current.get(entry.envelope.requestId);
406
+ return !(pendingCommand === null || pendingCommand === void 0 ? void 0 : pendingCommand.isStartup);
407
+ });
408
+ if (firstActionIndex === -1) {
409
+ return [...messages, validatedMessage];
410
+ }
411
+ return [
412
+ ...messages.slice(0, firstActionIndex),
413
+ validatedMessage,
414
+ ...messages.slice(firstActionIndex),
415
+ ];
416
+ });
417
+ return promise;
418
+ }, [instanceId, startPendingCommandTimeout]);
419
+ const reissueDeliveredPendingActionCommands = useCallback(() => {
420
+ var _a;
421
+ const replacements = new Map();
422
+ for (const [requestId, pendingCommand] of [
423
+ ...pendingCommandsRef.current.entries(),
424
+ ]) {
425
+ if (pendingCommand.isStartup || pendingCommand.hostEpoch === null) {
426
+ continue;
427
+ }
428
+ pendingCommandsRef.current.delete(requestId);
429
+ if (pendingCommand.timeout) {
430
+ clearTimeout(pendingCommand.timeout);
431
+ pendingCommand.timeout = null;
432
+ }
433
+ const command = pendingCommand.result.envelope.command;
434
+ const nextEnvelope = Object.assign(Object.assign({}, pendingCommand.result.envelope), { command: (_a = toRetryWithoutFlowHandle(command)) !== null && _a !== void 0 ? _a : command, requestId: createCommandRequestId() });
435
+ const nextMessage = parseWebViewTransportNativeMessage({
436
+ kind: "enqueueCommand",
437
+ id: createTransportMessageId(),
438
+ envelope: nextEnvelope,
439
+ });
440
+ const previousMessageId = pendingCommand.messageId;
441
+ pendingCommand.flowHandleCacheEpoch = flowHandleCacheEpochRef.current;
442
+ pendingCommand.hostEpoch = null;
443
+ pendingCommand.messageId = nextMessage.id;
444
+ pendingCommand.result = {
445
+ envelope: nextMessage.envelope,
446
+ message: nextMessage,
447
+ };
448
+ pendingCommandsRef.current.set(nextEnvelope.requestId, pendingCommand);
449
+ replacements.set(previousMessageId, nextMessage);
450
+ }
451
+ if (replacements.size === 0) {
452
+ return false;
453
+ }
454
+ setNativeMessages((messages) => messages.map((message) => { var _a; return (_a = replacements.get(message.id)) !== null && _a !== void 0 ? _a : message; }));
455
+ return true;
456
+ }, []);
457
+ const cancelPendingStartupCommands = useCallback(() => {
458
+ const canceledMessageIds = new Set();
459
+ for (const [requestId, pendingCommand] of pendingCommandsRef.current) {
460
+ if (!pendingCommand.isStartup) {
461
+ continue;
462
+ }
463
+ pendingCommandsRef.current.delete(requestId);
464
+ canceledMessageIds.add(pendingCommand.messageId);
465
+ if (pendingCommand.timeout) {
466
+ clearTimeout(pendingCommand.timeout);
467
+ }
468
+ pendingCommand.reject(new Error(STARTUP_COMMAND_REPLACED_ERROR_MESSAGE));
469
+ }
470
+ if (canceledMessageIds.size === 0) {
471
+ return;
472
+ }
473
+ setNativeMessages((messages) => messages.filter((entry) => !canceledMessageIds.has(entry.id)));
474
+ }, []);
475
+ const enqueueStartupCommands = useCallback(({ force = false } = {}) => {
476
+ if (!autoInit) {
477
+ return;
478
+ }
479
+ if (!force && startupQueuedKeyRef.current === startupOptionsKey) {
480
+ return;
481
+ }
482
+ const isReplacingStartup = force || startupQueuedKeyRef.current !== startupOptionsKey;
483
+ if (isReplacingStartup) {
484
+ cancelPendingStartupCommands();
485
+ }
486
+ startupQueuedKeyRef.current = startupOptionsKey;
487
+ enqueueTransportCommand({ kind: "init", opts: initOptionsRef.current }, { idempotencyKey: `${instanceId}:init:${startupIdempotencyKey}` }, { isStartup: true, prioritizeStartup: isReplacingStartup }).catch(reportCommandError);
488
+ if (configureOptionsRef.current === undefined) {
489
+ return;
490
+ }
491
+ enqueueTransportCommand({ kind: "configure", opts: configureOptionsRef.current }, { idempotencyKey: `${instanceId}:configure:${startupIdempotencyKey}` }, { isStartup: true, prioritizeStartup: isReplacingStartup }).catch(reportCommandError);
492
+ }, [
493
+ autoInit,
494
+ cancelPendingStartupCommands,
495
+ enqueueTransportCommand,
496
+ instanceId,
497
+ reportCommandError,
498
+ startupIdempotencyKey,
499
+ startupOptionsKey,
500
+ ]);
501
+ const enqueueCommand = useCallback((command, options) => {
502
+ enqueueStartupCommands();
503
+ return enqueueTransportCommand(command, options);
504
+ }, [enqueueStartupCommands, enqueueTransportCommand]);
505
+ const handleWidgetReady = useCallback((event) => {
506
+ const previous = lastReadyRef.current;
507
+ const alreadySeen = (previous === null || previous === void 0 ? void 0 : previous.sourceKey) === event.sourceKey &&
508
+ previous.readySequence === event.readySequence;
509
+ if (alreadySeen) {
510
+ return false;
511
+ }
512
+ const hasDeliveredPendingCommands = [
513
+ ...pendingCommandsRef.current.values(),
514
+ ].some((pendingCommand) => pendingCommand.hostEpoch !== null);
515
+ let didReissueDeliveredCommands = false;
516
+ if (previous !== null || hasDeliveredPendingCommands) {
517
+ hostEpochRef.current += 1;
518
+ startupQueuedKeyRef.current = null;
519
+ flowHandleCacheEpochRef.current += 1;
520
+ flowHandleIdsByFlowIdRef.current.clear();
521
+ didReissueDeliveredCommands = reissueDeliveredPendingActionCommands();
522
+ }
523
+ lastReadyRef.current = Object.assign(Object.assign({}, event), { hostEpoch: hostEpochRef.current });
524
+ const nextVisibility = updateProviderOverlayReadySequence({
525
+ ready: {
526
+ hostSourceVersion: hostSourceVersionRef.current,
527
+ readySequence: event.readySequence,
528
+ sourceKey: event.sourceKey,
529
+ },
530
+ visibility: providerOverlayStateRef.current.visibility,
531
+ });
532
+ providerOverlayStateRef.current = Object.assign(Object.assign({}, providerOverlayStateRef.current), { visibility: nextVisibility });
533
+ setWidgetHostVisibility(nextVisibility);
534
+ enqueueStartupCommands({ force: previous !== null });
535
+ startPendingCommandTimeouts();
536
+ return didReissueDeliveredCommands;
537
+ }, [
538
+ enqueueStartupCommands,
539
+ reissueDeliveredPendingActionCommands,
540
+ startPendingCommandTimeouts,
541
+ ]);
542
+ const handleWidgetReadyStateChange = useCallback((event) => {
543
+ if (event.isReady) {
544
+ const didReissueDeliveredCommands = handleWidgetReady({
545
+ readySequence: event.readySequence,
546
+ sourceKey: event.sourceKey,
547
+ });
548
+ return {
549
+ deferNativeMessageInjection: didReissueDeliveredCommands,
550
+ };
551
+ }
552
+ if (lastReadyRef.current !== null) {
553
+ hostEpochRef.current += 1;
554
+ startupQueuedKeyRef.current = null;
555
+ flowHandleCacheEpochRef.current += 1;
556
+ flowHandleIdsByFlowIdRef.current.clear();
557
+ reissueDeliveredPendingActionCommands();
558
+ }
559
+ lastReadyRef.current = null;
560
+ providerOverlayStateRef.current = createDefaultProviderOverlayState();
561
+ setWidgetHostVisibility(providerOverlayStateRef.current.visibility);
562
+ pausePendingCommandTimeouts();
563
+ }, [
564
+ handleWidgetReady,
565
+ pausePendingCommandTimeouts,
566
+ reissueDeliveredPendingActionCommands,
567
+ ]);
568
+ const deleteCachedFlowHandleId = useCallback((flowHandleId) => {
569
+ for (const [flowId, cachedFlowHandleId,] of flowHandleIdsByFlowIdRef.current) {
570
+ if (cachedFlowHandleId === flowHandleId) {
571
+ flowHandleIdsByFlowIdRef.current.delete(flowId);
572
+ }
573
+ }
574
+ flowHandleCacheEpochRef.current += 1;
575
+ }, []);
576
+ const handleFlowHandleInvalidated = useCallback((flowHandleId) => {
577
+ deleteCachedFlowHandleId(flowHandleId);
578
+ const nextState = applyProviderOverlayHandleInvalidated({
579
+ flowHandleId,
580
+ state: providerOverlayStateRef.current,
581
+ });
582
+ providerOverlayStateRef.current = nextState;
583
+ setWidgetHostVisibility(nextState.visibility);
584
+ }, [deleteCachedFlowHandleId]);
585
+ const handleFlowStateChanged = useCallback((detail, options) => {
586
+ if (detail.instanceId !== instanceId) {
587
+ return;
588
+ }
589
+ const readyState = lastReadyRef.current;
590
+ if (!readyState) {
591
+ return;
592
+ }
593
+ const nextState = applyProviderOverlayFlowState({
594
+ detail,
595
+ origin: options.origin,
596
+ ready: {
597
+ hostSourceVersion: hostSourceVersionRef.current,
598
+ readySequence: readyState.readySequence,
599
+ sourceKey: readyState.sourceKey,
600
+ },
601
+ state: providerOverlayStateRef.current,
602
+ });
603
+ providerOverlayStateRef.current = nextState;
604
+ setWidgetHostVisibility(nextState.visibility);
605
+ }, [instanceId]);
606
+ const handleWebMessage = useCallback((message) => {
607
+ var _a;
608
+ if (message.kind === "hostEvent" &&
609
+ message.event.name === "instance:handle:invalidated") {
610
+ handleFlowHandleInvalidated(message.event.detail.handleId);
611
+ }
612
+ if (message.kind === "hostEvent" &&
613
+ message.event.name === "instance:flow:state-changed") {
614
+ handleFlowStateChanged(message.event.detail, { origin: "handle" });
615
+ }
616
+ if (message.kind === "hostEvent" &&
617
+ message.event.name === "instance:flow:state-changed:instance") {
618
+ handleFlowStateChanged(message.event.detail, { origin: "aggregate" });
619
+ }
620
+ if (message.kind === "hostEvent" &&
621
+ message.event.name === "instance:command:settled") {
622
+ const requestId = message.event.detail.requestId;
623
+ const pendingCommand = pendingCommandsRef.current.get(requestId);
624
+ if (pendingCommand) {
625
+ if (pendingCommand.hostEpoch !== null &&
626
+ pendingCommand.hostEpoch !== ((_a = lastReadyRef.current) === null || _a === void 0 ? void 0 : _a.hostEpoch)) {
627
+ onWebMessage === null || onWebMessage === void 0 ? void 0 : onWebMessage(message);
628
+ return;
629
+ }
630
+ pendingCommandsRef.current.delete(requestId);
631
+ setNativeMessages((messages) => messages.filter((entry) => entry.id !== pendingCommand.messageId));
632
+ if (pendingCommand.timeout) {
633
+ clearTimeout(pendingCommand.timeout);
634
+ }
635
+ const error = toCommandSettlementError(message.event);
636
+ if (error) {
637
+ if (pendingCommand.isStartup) {
638
+ startupQueuedKeyRef.current = null;
639
+ }
640
+ const flowId = toCommandFlowId(pendingCommand.result.envelope.command);
641
+ const isStaleFlowHandleError = error.code === "STALE_HANDLE" ||
642
+ error.message.includes("flowHandleId is stale");
643
+ if (flowId && isStaleFlowHandleError) {
644
+ flowHandleIdsByFlowIdRef.current.delete(flowId);
645
+ }
646
+ const retryCommand = isStaleFlowHandleError
647
+ ? toRetryWithoutFlowHandle(pendingCommand.result.envelope.command)
648
+ : null;
649
+ if (retryCommand) {
650
+ enqueueTransportCommand(retryCommand).then(pendingCommand.resolve, pendingCommand.reject);
651
+ return;
652
+ }
653
+ pendingCommand.reject(error);
654
+ }
655
+ else {
656
+ const settlement = message.event
657
+ .detail;
658
+ const flowId = toCommandFlowId(pendingCommand.result.envelope.command);
659
+ const flowHandleId = toSettledFlowHandleId(settlement.result);
660
+ const canWriteFlowHandle = flowId &&
661
+ flowHandleId &&
662
+ pendingCommand.flowHandleCacheEpoch ===
663
+ flowHandleCacheEpochRef.current &&
664
+ pendingCommand.flowCommandSequence ===
665
+ flowCommandSequencesByFlowIdRef.current.get(flowId);
666
+ if (canWriteFlowHandle) {
667
+ flowHandleIdsByFlowIdRef.current.set(flowId, flowHandleId);
668
+ }
669
+ if (pendingCommand.result.envelope.command.kind === "reset") {
670
+ flowHandleCacheEpochRef.current += 1;
671
+ flowHandleIdsByFlowIdRef.current.clear();
672
+ }
673
+ const settledResult = Object.assign(Object.assign({}, pendingCommand.result), { settlement, settlementResult: settlement.result });
674
+ pendingCommand.resolve(settledResult);
675
+ }
676
+ }
677
+ }
678
+ onWebMessage === null || onWebMessage === void 0 ? void 0 : onWebMessage(message);
679
+ }, [
680
+ enqueueTransportCommand,
681
+ handleFlowStateChanged,
682
+ handleFlowHandleInvalidated,
7
683
  onWebMessage,
8
- }), [configureOptions, initOptions, onWebMessage]);
684
+ ]);
685
+ useEffect(() => () => {
686
+ for (const pendingCommand of pendingCommandsRef.current.values()) {
687
+ if (pendingCommand.timeout) {
688
+ clearTimeout(pendingCommand.timeout);
689
+ }
690
+ pendingCommand.reject(new Error(PROVIDER_UNMOUNTED_ERROR_MESSAGE));
691
+ }
692
+ pendingCommandsRef.current.clear();
693
+ }, []);
694
+ useEffect(() => {
695
+ if (visibilitySourceKeyRef.current === hostSourceKey) {
696
+ return;
697
+ }
698
+ visibilitySourceKeyRef.current = hostSourceKey;
699
+ providerOverlayStateRef.current = createDefaultProviderOverlayState();
700
+ setWidgetHostVisibility(providerOverlayStateRef.current.visibility);
701
+ }, [hostSourceKey]);
702
+ const contextValue = useMemo(() => ({
703
+ instanceId,
704
+ get nativeMessages() {
705
+ return nativeMessagesRef.current;
706
+ },
707
+ enqueueCommand,
708
+ configure: (opts, options) => enqueueCommand({ kind: "configure", opts }, options),
709
+ identify: ((identifyInput, traitsOrOptions, options) => enqueueCommand(toIdentifyCommandPayload(identifyInput, traitsOrOptions, options))),
710
+ track: (eventName, properties, options) => {
711
+ assertNoSegmentExternalIdsInValueArgument({
712
+ argument: properties,
713
+ commandName: "track",
714
+ valueArgumentName: "properties",
715
+ callShape: "track(eventName, properties, { externalIds })",
716
+ });
717
+ return enqueueCommand(Object.assign(Object.assign({ kind: "track", origin: "customer", event: resolveRequiredString("eventName", eventName) }, (properties === undefined ? {} : { properties })), ((options === null || options === void 0 ? void 0 : options.externalIds) === undefined
718
+ ? {}
719
+ : { context: { externalIds: options.externalIds } })));
720
+ },
721
+ open: (flowId, options, commandOptions) => {
722
+ const normalizedFlowId = resolveRequiredString("flowId", flowId);
723
+ return enqueueCommand({
724
+ kind: "open",
725
+ flowId: normalizedFlowId,
726
+ flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
727
+ hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
728
+ }, commandOptions);
729
+ },
730
+ prefetch: (flowId, options) => {
731
+ const normalizedFlowId = resolveRequiredString("flowId", flowId);
732
+ return enqueueCommand({
733
+ kind: "prefetch",
734
+ flowId: normalizedFlowId,
735
+ flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
736
+ }, options);
737
+ },
738
+ prerender: (flowId, options, commandOptions) => {
739
+ const normalizedFlowId = resolveRequiredString("flowId", flowId);
740
+ return enqueueCommand({
741
+ kind: "prerender",
742
+ flowId: normalizedFlowId,
743
+ flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
744
+ hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
745
+ }, commandOptions);
746
+ },
747
+ close: (flowHandleId, options) => enqueueCommand(Object.assign({ kind: "close" }, (flowHandleId === undefined
748
+ ? {}
749
+ : {
750
+ flowHandleId: resolveRequiredString("flowHandleId", flowHandleId),
751
+ })), options),
752
+ reset: (options) => enqueueCommand({ kind: "reset" }, options),
753
+ updateHostContext: (context, options) => enqueueCommand({ kind: "updateHostContext", context }, options),
754
+ emitHostSignal: (name, data, options) => enqueueCommand(Object.assign({ kind: "emitHostSignal", name: resolveRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
755
+ handleWebMessage,
756
+ }), [enqueueCommand, handleWebMessage, instanceId]);
757
+ useEffect(() => {
758
+ if (startupQueuedKeyRef.current !== startupOptionsKey) {
759
+ startupQueuedKeyRef.current = null;
760
+ }
761
+ enqueueStartupCommands();
762
+ }, [enqueueStartupCommands, startupOptionsKey]);
763
+ const isWidgetHostVisible = widgetHostVisibility.isVisible &&
764
+ widgetHostVisibility.hostSourceVersion === hostSourceVersionRef.current &&
765
+ widgetHostVisibility.sourceKey === ((_a = lastReadyRef.current) === null || _a === void 0 ? void 0 : _a.sourceKey) &&
766
+ widgetHostVisibility.readySequence === ((_b = lastReadyRef.current) === null || _b === void 0 ? void 0 : _b.readySequence);
767
+ const sheetHostViewport = toSheetHostViewport(hostViewport !== null && hostViewport !== void 0 ? hostViewport : resolvedHostViewport);
768
+ const widgetHostViewport = isWidgetHostVisible
769
+ ? sheetHostViewport
770
+ : (hostViewport !== null && hostViewport !== void 0 ? hostViewport : resolvedHostViewport);
771
+ const widgetSheetStyle = sheetHostViewport
772
+ ? Object.assign(Object.assign({}, WIDGET_SHEET_STYLE), { height: sheetHostViewport.height }) : Object.assign(Object.assign({}, WIDGET_SHEET_STYLE), { height: "86%" });
773
+ const widgetHostStyle = isWidgetHostVisible
774
+ ? VISIBLE_WIDGET_HOST_STYLE
775
+ : HIDDEN_WIDGET_HOST_STYLE;
776
+ const visiblePresentationStyle = isWidgetHostVisible
777
+ ? toVisibleWidgetHostPresentationStyle(webViewProps === null || webViewProps === void 0 ? void 0 : webViewProps.style)
778
+ : undefined;
779
+ const composedWebViewStyle = isWidgetHostVisible
780
+ ? visiblePresentationStyle !== undefined
781
+ ? [visiblePresentationStyle, widgetHostStyle]
782
+ : widgetHostStyle
783
+ : ((_c = webViewProps === null || webViewProps === void 0 ? void 0 : webViewProps.style) !== null && _c !== void 0 ? _c : widgetHostStyle);
784
+ const widgetHost = createElement(WidgetHost, {
785
+ hostViewport: widgetHostViewport,
786
+ instanceId,
787
+ loaderUrl,
788
+ nativeMessages,
789
+ onInvalidWebMessage,
790
+ onReadyStateChange: handleWidgetReadyStateChange,
791
+ onWebMessage: handleWebMessage,
792
+ source,
793
+ webViewComponent,
794
+ webViewProps: Object.assign(Object.assign({}, webViewProps), { pointerEvents: isWidgetHostVisible ? "auto" : "none", style: composedWebViewStyle }),
795
+ });
9
796
  return createElement(GetUserFeedbackNativeContext.Provider, {
10
797
  value: contextValue,
11
- }, children);
798
+ }, children, createElement(NativeView, {
799
+ pointerEvents: isWidgetHostVisible ? "auto" : "none",
800
+ style: isWidgetHostVisible
801
+ ? WIDGET_SHEET_OVERLAY_STYLE
802
+ : HIDDEN_WIDGET_HOST_STYLE,
803
+ testID: "gx-widget-host-overlay",
804
+ }, createElement(NativeView, {
805
+ pointerEvents: isWidgetHostVisible ? "auto" : "none",
806
+ style: isWidgetHostVisible
807
+ ? WIDGET_SHEET_BACKDROP_STYLE
808
+ : HIDDEN_WIDGET_HOST_STYLE,
809
+ testID: "gx-widget-host-backdrop",
810
+ }), createElement(NativeView, {
811
+ pointerEvents: isWidgetHostVisible ? "auto" : "none",
812
+ style: isWidgetHostVisible
813
+ ? widgetSheetStyle
814
+ : HIDDEN_WIDGET_HOST_STYLE,
815
+ testID: "gx-widget-host-sheet",
816
+ }, widgetHost)));
12
817
  }
13
818
  export function useGetUserFeedbackNative() {
14
819
  const context = useContext(GetUserFeedbackNativeContext);
@@ -17,6 +822,4 @@ export function useGetUserFeedbackNative() {
17
822
  }
18
823
  return context;
19
824
  }
20
- export function WidgetHost(_props) {
21
- return null;
22
- }
825
+ export const useGetUserFeedback = useGetUserFeedbackNative;