@paymanai/payman-ask-sdk 4.0.17 → 4.0.18
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 +11 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -0
- package/dist/index.mjs.map +1 -1
- package/dist/index.native.js +960 -24
- package/dist/index.native.js.map +1 -1
- package/package.json +1 -1
package/dist/index.native.js
CHANGED
|
@@ -868,6 +868,17 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
|
|
|
868
868
|
signal: abortController.signal,
|
|
869
869
|
onEvent: (event) => {
|
|
870
870
|
if (abortController.signal.aborted) return;
|
|
871
|
+
try {
|
|
872
|
+
const et = event?.eventType;
|
|
873
|
+
if (et === "RUN_IN_PROGRESS" || et === "INTENT_PROGRESS" || et === "THINKING_DELTA") {
|
|
874
|
+
const len = (event.partialText ?? event.text ?? "").length;
|
|
875
|
+
console.log(`[stream] ${et} (+${len} chars)`);
|
|
876
|
+
} else {
|
|
877
|
+
console.log(`[stream] ${et ?? "?"}:`, JSON.stringify(event));
|
|
878
|
+
}
|
|
879
|
+
} catch {
|
|
880
|
+
console.log("[stream] (unserializable event)", event?.eventType);
|
|
881
|
+
}
|
|
871
882
|
processStreamEventV2(event, state);
|
|
872
883
|
if (state.lastUserAction) {
|
|
873
884
|
callbacksRef.current.onUserActionRequired?.(state.lastUserAction);
|
|
@@ -1687,6 +1698,122 @@ function useVoice(config = {}, callbacks = {}) {
|
|
|
1687
1698
|
reset
|
|
1688
1699
|
};
|
|
1689
1700
|
}
|
|
1701
|
+
function classifyField(field) {
|
|
1702
|
+
if (!field) return "text";
|
|
1703
|
+
if (Array.isArray(field.oneOf) && field.oneOf.length > 0) return "select";
|
|
1704
|
+
switch (field.type) {
|
|
1705
|
+
case "boolean":
|
|
1706
|
+
return "boolean";
|
|
1707
|
+
case "integer":
|
|
1708
|
+
return "integer";
|
|
1709
|
+
case "number":
|
|
1710
|
+
return "decimal";
|
|
1711
|
+
case "string":
|
|
1712
|
+
return "text";
|
|
1713
|
+
default:
|
|
1714
|
+
return "text";
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
function isNestedOrUnsupported(field) {
|
|
1718
|
+
if (!field) return false;
|
|
1719
|
+
if (field.type === "object" || field.type === "array") return true;
|
|
1720
|
+
if ("properties" in field && field.properties != null) return true;
|
|
1721
|
+
if ("items" in field && field.items != null) return true;
|
|
1722
|
+
return false;
|
|
1723
|
+
}
|
|
1724
|
+
function getOptions(field) {
|
|
1725
|
+
if (!field || !Array.isArray(field.oneOf)) return [];
|
|
1726
|
+
return field.oneOf.filter(
|
|
1727
|
+
(o) => !!o && typeof o === "object" && typeof o.const === "string"
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
function isRequired(schema, key) {
|
|
1731
|
+
return Array.isArray(schema?.required) && schema.required.includes(key);
|
|
1732
|
+
}
|
|
1733
|
+
function coerceValue(field, raw) {
|
|
1734
|
+
const widget = classifyField(field);
|
|
1735
|
+
if (widget === "boolean") {
|
|
1736
|
+
if (typeof raw === "boolean") return raw;
|
|
1737
|
+
if (raw === "true") return true;
|
|
1738
|
+
if (raw === "false") return false;
|
|
1739
|
+
return Boolean(raw);
|
|
1740
|
+
}
|
|
1741
|
+
if (widget === "integer" || widget === "decimal") {
|
|
1742
|
+
if (raw === "" || raw == null) return void 0;
|
|
1743
|
+
const num = typeof raw === "number" ? raw : Number(String(raw).trim());
|
|
1744
|
+
if (Number.isNaN(num)) return void 0;
|
|
1745
|
+
return widget === "integer" ? Math.trunc(num) : num;
|
|
1746
|
+
}
|
|
1747
|
+
if (raw == null) return void 0;
|
|
1748
|
+
const str = String(raw);
|
|
1749
|
+
return str === "" ? void 0 : str;
|
|
1750
|
+
}
|
|
1751
|
+
function defaultValueFor(field) {
|
|
1752
|
+
if (!field || field.default === void 0) {
|
|
1753
|
+
return classifyField(field) === "boolean" ? false : "";
|
|
1754
|
+
}
|
|
1755
|
+
return field.default;
|
|
1756
|
+
}
|
|
1757
|
+
function validateField(field, value, required) {
|
|
1758
|
+
const widget = classifyField(field);
|
|
1759
|
+
const label = field?.title || "This field";
|
|
1760
|
+
const isEmpty = value === void 0 || value === null || typeof value === "string" && value.trim() === "";
|
|
1761
|
+
if (isEmpty) {
|
|
1762
|
+
if (required && widget !== "boolean") return `${label} is required.`;
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
if (widget === "integer" || widget === "decimal") {
|
|
1766
|
+
const num = typeof value === "number" ? value : Number(value);
|
|
1767
|
+
if (Number.isNaN(num)) return `${label} must be a number.`;
|
|
1768
|
+
if (widget === "integer" && !Number.isInteger(num)) {
|
|
1769
|
+
return `${label} must be a whole number.`;
|
|
1770
|
+
}
|
|
1771
|
+
if (typeof field?.minimum === "number" && num < field.minimum) {
|
|
1772
|
+
return `${label} must be at least ${field.minimum}.`;
|
|
1773
|
+
}
|
|
1774
|
+
if (typeof field?.maximum === "number" && num > field.maximum) {
|
|
1775
|
+
return `${label} must be at most ${field.maximum}.`;
|
|
1776
|
+
}
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
if (widget === "select") {
|
|
1780
|
+
const allowed = getOptions(field).map((o) => o.const);
|
|
1781
|
+
if (allowed.length > 0 && !allowed.includes(String(value))) {
|
|
1782
|
+
return `${label} has an invalid selection.`;
|
|
1783
|
+
}
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1786
|
+
const str = String(value);
|
|
1787
|
+
if (typeof field?.minLength === "number" && str.length < field.minLength) {
|
|
1788
|
+
return `${label} must be at least ${field.minLength} characters.`;
|
|
1789
|
+
}
|
|
1790
|
+
if (typeof field?.maxLength === "number" && str.length > field.maxLength) {
|
|
1791
|
+
return `${label} must be at most ${field.maxLength} characters.`;
|
|
1792
|
+
}
|
|
1793
|
+
return null;
|
|
1794
|
+
}
|
|
1795
|
+
function renderableFields(schema) {
|
|
1796
|
+
const props = schema?.properties;
|
|
1797
|
+
if (!props) return [];
|
|
1798
|
+
return Object.entries(props).filter(([, field]) => !isNestedOrUnsupported(field));
|
|
1799
|
+
}
|
|
1800
|
+
function validateForm(schema, values) {
|
|
1801
|
+
const errors = {};
|
|
1802
|
+
for (const [key, field] of renderableFields(schema)) {
|
|
1803
|
+
const coerced = coerceValue(field, values[key]);
|
|
1804
|
+
const err = validateField(field, coerced, isRequired(schema, key));
|
|
1805
|
+
if (err) errors[key] = err;
|
|
1806
|
+
}
|
|
1807
|
+
return errors;
|
|
1808
|
+
}
|
|
1809
|
+
function buildContent(schema, values) {
|
|
1810
|
+
const content = {};
|
|
1811
|
+
for (const [key, field] of renderableFields(schema)) {
|
|
1812
|
+
const coerced = coerceValue(field, values[key]);
|
|
1813
|
+
if (coerced !== void 0) content[key] = coerced;
|
|
1814
|
+
}
|
|
1815
|
+
return content;
|
|
1816
|
+
}
|
|
1690
1817
|
var PaymanChatContext = react.createContext(void 0);
|
|
1691
1818
|
function usePaymanChat() {
|
|
1692
1819
|
const context = react.useContext(PaymanChatContext);
|
|
@@ -1767,7 +1894,8 @@ function InputBar({
|
|
|
1767
1894
|
onFocus,
|
|
1768
1895
|
insetBottom
|
|
1769
1896
|
}) {
|
|
1770
|
-
const canSend = !disabled && value.trim().length > 0;
|
|
1897
|
+
const canSend = !disabled && !isStreaming && value.trim().length > 0;
|
|
1898
|
+
const voiceDisabled = !voiceAvailable || disabled || isStreaming;
|
|
1771
1899
|
const showVoiceBar = enableVoice && isRecording;
|
|
1772
1900
|
const showVoiceBtn = enableVoice && !isRecording;
|
|
1773
1901
|
const [keyboardOpen, setKeyboardOpen] = react.useState(false);
|
|
@@ -1875,12 +2003,8 @@ function InputBar({
|
|
|
1875
2003
|
reactNative.Pressable,
|
|
1876
2004
|
{
|
|
1877
2005
|
onPress: onVoicePress,
|
|
1878
|
-
disabled:
|
|
1879
|
-
style:
|
|
1880
|
-
s.iconBtn,
|
|
1881
|
-
(!voiceAvailable || disabled) && s.btnDisabled,
|
|
1882
|
-
pressed && s.pressed
|
|
1883
|
-
],
|
|
2006
|
+
disabled: voiceDisabled,
|
|
2007
|
+
style: [s.iconBtn, voiceDisabled && s.btnDisabled],
|
|
1884
2008
|
accessibilityLabel: "Voice input",
|
|
1885
2009
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.iconBtnInner, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Mic, { size: 16, color: "#6B7280", strokeWidth: 2 }) })
|
|
1886
2010
|
}
|
|
@@ -1888,13 +2012,9 @@ function InputBar({
|
|
|
1888
2012
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1889
2013
|
reactNative.Pressable,
|
|
1890
2014
|
{
|
|
1891
|
-
onPress:
|
|
1892
|
-
disabled: !
|
|
1893
|
-
style:
|
|
1894
|
-
s.sendBtn,
|
|
1895
|
-
!isStreaming && !canSend && s.btnDisabled,
|
|
1896
|
-
pressed && s.pressed
|
|
1897
|
-
],
|
|
2015
|
+
onPress: onSend,
|
|
2016
|
+
disabled: !canSend,
|
|
2017
|
+
style: [s.sendBtn, !canSend && s.btnDisabled],
|
|
1898
2018
|
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [s.sendBtnInner, { backgroundColor: accent }], children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Send, { size: 14, color: "#FFFFFF" }) })
|
|
1899
2019
|
}
|
|
1900
2020
|
)
|
|
@@ -2161,11 +2281,27 @@ function stripIncompleteImageToken(text) {
|
|
|
2161
2281
|
if (/^!\[[^\]]*\]\([^)]*\)/.test(after)) return text;
|
|
2162
2282
|
return text.slice(0, lastBang);
|
|
2163
2283
|
}
|
|
2284
|
+
function stripDuplicatePromptText(text, promptText) {
|
|
2285
|
+
if (!promptText) return text;
|
|
2286
|
+
const pm = promptText.replace(/\\n/g, "\n").trim();
|
|
2287
|
+
if (!pm) return text;
|
|
2288
|
+
const idx = text.indexOf(pm);
|
|
2289
|
+
if (idx !== -1) {
|
|
2290
|
+
return (text.slice(0, idx) + text.slice(idx + pm.length)).trim();
|
|
2291
|
+
}
|
|
2292
|
+
for (let cut = Math.min(text.length, pm.length); cut >= 24; cut--) {
|
|
2293
|
+
if (text.endsWith(pm.slice(0, cut))) {
|
|
2294
|
+
return text.slice(0, text.length - cut).trim();
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
return text;
|
|
2298
|
+
}
|
|
2164
2299
|
function AssistantBubble({
|
|
2165
2300
|
message,
|
|
2166
2301
|
shouldType,
|
|
2167
2302
|
isDark,
|
|
2168
|
-
accent
|
|
2303
|
+
accent,
|
|
2304
|
+
suppressText
|
|
2169
2305
|
}) {
|
|
2170
2306
|
const isCurrentlyStreaming = !!message.isStreaming && !message.isCancelled;
|
|
2171
2307
|
const mdStyles = isDark ? MD_STYLES_DARK : MD_STYLES_LIGHT;
|
|
@@ -2176,7 +2312,8 @@ function AssistantBubble({
|
|
|
2176
2312
|
const raw = message.isStreaming ? message.streamingContent || message.content : message.content;
|
|
2177
2313
|
if (!raw) return "";
|
|
2178
2314
|
const normalized = raw.replace(/\\n/g, "\n");
|
|
2179
|
-
|
|
2315
|
+
const cleaned = message.isStreaming ? stripIncompleteImageToken(normalized) : normalized;
|
|
2316
|
+
return stripDuplicatePromptText(cleaned, suppressText);
|
|
2180
2317
|
})();
|
|
2181
2318
|
const isThinkingStreaming = isCurrentlyStreaming && !rawResponseContent && !message.isError;
|
|
2182
2319
|
const hasEverStreamed = react.useRef(!!message.isStreaming);
|
|
@@ -2206,10 +2343,740 @@ function AssistantBubble({
|
|
|
2206
2343
|
message.isError && displayedText && /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: sd.partialErrorText, children: message.errorDetails || "The response was interrupted." })
|
|
2207
2344
|
] }) });
|
|
2208
2345
|
}
|
|
2209
|
-
function MessageBubble({ message, accent, shouldType, isDark }) {
|
|
2346
|
+
function MessageBubble({ message, accent, shouldType, isDark, suppressText }) {
|
|
2210
2347
|
if (message.role === "user") return /* @__PURE__ */ jsxRuntime.jsx(UserBubble, { message, accent });
|
|
2211
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AssistantBubble, { message, shouldType, isDark, accent });
|
|
2348
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AssistantBubble, { message, shouldType, isDark, accent, suppressText });
|
|
2349
|
+
}
|
|
2350
|
+
var RESEND_COOLDOWN_S = 30;
|
|
2351
|
+
var DEFAULT_CODE_LEN = 6;
|
|
2352
|
+
function uaPalette(isDark) {
|
|
2353
|
+
return isDark ? {
|
|
2354
|
+
sheetBg: "#13201f",
|
|
2355
|
+
text: "rgba(255,255,255,0.95)",
|
|
2356
|
+
muted: "rgba(255,255,255,0.55)",
|
|
2357
|
+
border: "rgba(255,255,255,0.10)",
|
|
2358
|
+
fieldBg: "rgba(255,255,255,0.06)",
|
|
2359
|
+
fieldBorder: "rgba(255,255,255,0.18)",
|
|
2360
|
+
overlay: "rgba(0,0,0,0.62)",
|
|
2361
|
+
grabber: "rgba(255,255,255,0.22)"
|
|
2362
|
+
} : {
|
|
2363
|
+
sheetBg: "#ffffff",
|
|
2364
|
+
text: "#111827",
|
|
2365
|
+
muted: "#6b7280",
|
|
2366
|
+
border: "#eceef1",
|
|
2367
|
+
fieldBg: "#f9fafb",
|
|
2368
|
+
fieldBorder: "#d1d5db",
|
|
2369
|
+
overlay: "rgba(15,23,42,0.45)",
|
|
2370
|
+
grabber: "rgba(15,23,42,0.18)"
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
function promptInitialSeconds(prompt) {
|
|
2374
|
+
return prompt && typeof prompt.expirySeconds === "number" && prompt.expirySeconds > 0 ? Math.floor(prompt.expirySeconds) : void 0;
|
|
2375
|
+
}
|
|
2376
|
+
function promptTimerKey(prompt) {
|
|
2377
|
+
return prompt ? `${prompt.userActionId}|${prompt.subAction ?? ""}` : "";
|
|
2378
|
+
}
|
|
2379
|
+
function useExpiredFlag(prompt) {
|
|
2380
|
+
const initial = promptInitialSeconds(prompt);
|
|
2381
|
+
const key = promptTimerKey(prompt);
|
|
2382
|
+
const [expired, setExpired] = react.useState(false);
|
|
2383
|
+
react.useEffect(() => {
|
|
2384
|
+
setExpired(false);
|
|
2385
|
+
if (initial === void 0) return;
|
|
2386
|
+
const t = setTimeout(() => setExpired(true), initial * 1e3);
|
|
2387
|
+
return () => clearTimeout(t);
|
|
2388
|
+
}, [key, initial]);
|
|
2389
|
+
return expired;
|
|
2390
|
+
}
|
|
2391
|
+
function useSecondsLeft(expirySeconds, restartKey) {
|
|
2392
|
+
const initial = typeof expirySeconds === "number" && expirySeconds > 0 ? Math.floor(expirySeconds) : void 0;
|
|
2393
|
+
const [left, setLeft] = react.useState(initial);
|
|
2394
|
+
react.useEffect(() => {
|
|
2395
|
+
setLeft(initial);
|
|
2396
|
+
if (initial === void 0) return;
|
|
2397
|
+
const id = setInterval(() => {
|
|
2398
|
+
setLeft((s2) => s2 === void 0 ? s2 : s2 <= 1 ? 0 : s2 - 1);
|
|
2399
|
+
}, 1e3);
|
|
2400
|
+
return () => clearInterval(id);
|
|
2401
|
+
}, [restartKey, initial]);
|
|
2402
|
+
return left;
|
|
2403
|
+
}
|
|
2404
|
+
function SheetTimerPill({
|
|
2405
|
+
prompt,
|
|
2406
|
+
accent,
|
|
2407
|
+
isDark
|
|
2408
|
+
}) {
|
|
2409
|
+
const left = useSecondsLeft(prompt.expirySeconds, promptTimerKey(prompt));
|
|
2410
|
+
if (left === void 0 || prompt.status === "stale") return null;
|
|
2411
|
+
const expired = left <= 0;
|
|
2412
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2413
|
+
reactNative.View,
|
|
2414
|
+
{
|
|
2415
|
+
style: [
|
|
2416
|
+
sht.timer,
|
|
2417
|
+
{
|
|
2418
|
+
borderColor: expired ? "#ef4444" : accent + "40",
|
|
2419
|
+
backgroundColor: expired ? "rgba(239,68,68,0.10)" : accent + (isDark ? "1F" : "12")
|
|
2420
|
+
}
|
|
2421
|
+
],
|
|
2422
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: expired ? "#ef4444" : accent, fontSize: 12.5, fontWeight: "700", letterSpacing: 0.2 }, children: expired ? "Expired" : `${left}s` })
|
|
2423
|
+
}
|
|
2424
|
+
);
|
|
2425
|
+
}
|
|
2426
|
+
function CardTimerPill({
|
|
2427
|
+
prompt,
|
|
2428
|
+
accent,
|
|
2429
|
+
isDark
|
|
2430
|
+
}) {
|
|
2431
|
+
const left = useSecondsLeft(prompt.expirySeconds, promptTimerKey(prompt));
|
|
2432
|
+
if (left === void 0 || left <= 0) return null;
|
|
2433
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [rc.timer, { backgroundColor: accent + (isDark ? "26" : "14") }], children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: { color: accent, fontSize: 12, fontWeight: "700" }, children: [
|
|
2434
|
+
left,
|
|
2435
|
+
"s"
|
|
2436
|
+
] }) });
|
|
2437
|
+
}
|
|
2438
|
+
function CodeInput({
|
|
2439
|
+
value,
|
|
2440
|
+
onChange,
|
|
2441
|
+
length,
|
|
2442
|
+
disabled,
|
|
2443
|
+
error,
|
|
2444
|
+
accent,
|
|
2445
|
+
pal
|
|
2446
|
+
}) {
|
|
2447
|
+
const ref = react.useRef(null);
|
|
2448
|
+
react.useEffect(() => {
|
|
2449
|
+
const t = setTimeout(() => ref.current?.focus(), 380);
|
|
2450
|
+
return () => clearTimeout(t);
|
|
2451
|
+
}, []);
|
|
2452
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Pressable, { onPress: () => ref.current?.focus(), style: ub.codeRow, children: [
|
|
2453
|
+
Array.from({ length }).map((_, i) => {
|
|
2454
|
+
const ch = value[i] ?? "";
|
|
2455
|
+
const isCursor = i === value.length && !disabled;
|
|
2456
|
+
const bc = error ? "#ef4444" : isCursor ? accent : pal.fieldBorder;
|
|
2457
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [ub.codeBox, { borderColor: bc, backgroundColor: pal.fieldBg }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.codeChar, { color: pal.text }], children: ch }) }, i);
|
|
2458
|
+
}),
|
|
2459
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2460
|
+
reactNative.TextInput,
|
|
2461
|
+
{
|
|
2462
|
+
ref,
|
|
2463
|
+
value,
|
|
2464
|
+
onChangeText: (t) => onChange(t.replace(/[^0-9]/g, "").slice(0, length)),
|
|
2465
|
+
keyboardType: "number-pad",
|
|
2466
|
+
maxLength: length,
|
|
2467
|
+
editable: !disabled,
|
|
2468
|
+
caretHidden: true,
|
|
2469
|
+
style: ub.codeHidden
|
|
2470
|
+
}
|
|
2471
|
+
)
|
|
2472
|
+
] });
|
|
2473
|
+
}
|
|
2474
|
+
function PrimaryButton({
|
|
2475
|
+
label,
|
|
2476
|
+
onPress,
|
|
2477
|
+
disabled,
|
|
2478
|
+
accent
|
|
2479
|
+
}) {
|
|
2480
|
+
const bg = accent || DEFAULT_ACCENT;
|
|
2481
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2482
|
+
reactNative.Pressable,
|
|
2483
|
+
{
|
|
2484
|
+
onPress,
|
|
2485
|
+
disabled,
|
|
2486
|
+
android_ripple: { color: "rgba(255,255,255,0.22)" },
|
|
2487
|
+
style: [ub.primaryBtn, { backgroundColor: bg, borderColor: bg, opacity: disabled ? 0.5 : 1 }],
|
|
2488
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: ub.primaryBtnText, children: label })
|
|
2489
|
+
}
|
|
2490
|
+
);
|
|
2491
|
+
}
|
|
2492
|
+
function VerificationBody({
|
|
2493
|
+
prompt,
|
|
2494
|
+
expired,
|
|
2495
|
+
pal,
|
|
2496
|
+
accent,
|
|
2497
|
+
onSubmit,
|
|
2498
|
+
onCancel,
|
|
2499
|
+
onResend
|
|
2500
|
+
}) {
|
|
2501
|
+
const isNumeric = prompt.verificationType !== "ALPHANUMERIC_CODE";
|
|
2502
|
+
const field = prompt.requestedSchema?.properties?.verificationCode;
|
|
2503
|
+
const codeLen = (typeof field?.maxLength === "number" ? field.maxLength : void 0) ?? (typeof field?.minLength === "number" ? field.minLength : void 0) ?? DEFAULT_CODE_LEN;
|
|
2504
|
+
const [code, setCode] = react.useState("");
|
|
2505
|
+
const [errored, setErrored] = react.useState(false);
|
|
2506
|
+
const [resendSec, setResendSec] = react.useState(0);
|
|
2507
|
+
const lastSubmitted = react.useRef(null);
|
|
2508
|
+
const resendTimer = react.useRef(null);
|
|
2509
|
+
const busy = prompt.status === "submitting";
|
|
2510
|
+
const stale = prompt.status === "stale";
|
|
2511
|
+
const locked = busy || stale || expired;
|
|
2512
|
+
react.useEffect(() => {
|
|
2513
|
+
if (prompt.subAction === "SubmissionInvalid") {
|
|
2514
|
+
setErrored(true);
|
|
2515
|
+
setCode("");
|
|
2516
|
+
lastSubmitted.current = null;
|
|
2517
|
+
}
|
|
2518
|
+
}, [prompt.subAction, prompt.userActionId]);
|
|
2519
|
+
const doSubmit = react.useCallback(
|
|
2520
|
+
(value) => {
|
|
2521
|
+
if (locked || !value) return;
|
|
2522
|
+
if (lastSubmitted.current === value) return;
|
|
2523
|
+
lastSubmitted.current = value;
|
|
2524
|
+
void onSubmit(prompt.userActionId, { verificationCode: value }).catch(() => {
|
|
2525
|
+
lastSubmitted.current = null;
|
|
2526
|
+
setErrored(true);
|
|
2527
|
+
});
|
|
2528
|
+
},
|
|
2529
|
+
[locked, onSubmit, prompt.userActionId]
|
|
2530
|
+
);
|
|
2531
|
+
react.useEffect(() => {
|
|
2532
|
+
if (!isNumeric || locked) return;
|
|
2533
|
+
if (code.length === codeLen && /^\d+$/.test(code)) doSubmit(code);
|
|
2534
|
+
}, [code, codeLen, doSubmit, isNumeric, locked]);
|
|
2535
|
+
react.useEffect(() => () => {
|
|
2536
|
+
if (resendTimer.current) clearInterval(resendTimer.current);
|
|
2537
|
+
}, []);
|
|
2538
|
+
const handleResend = react.useCallback(async () => {
|
|
2539
|
+
if (locked || resendSec > 0) return;
|
|
2540
|
+
setErrored(false);
|
|
2541
|
+
setCode("");
|
|
2542
|
+
lastSubmitted.current = null;
|
|
2543
|
+
try {
|
|
2544
|
+
await onResend(prompt.userActionId);
|
|
2545
|
+
setResendSec(RESEND_COOLDOWN_S);
|
|
2546
|
+
if (resendTimer.current) clearInterval(resendTimer.current);
|
|
2547
|
+
resendTimer.current = setInterval(() => {
|
|
2548
|
+
setResendSec((s2) => {
|
|
2549
|
+
if (s2 <= 1) {
|
|
2550
|
+
if (resendTimer.current) clearInterval(resendTimer.current);
|
|
2551
|
+
return 0;
|
|
2552
|
+
}
|
|
2553
|
+
return s2 - 1;
|
|
2554
|
+
});
|
|
2555
|
+
}, 1e3);
|
|
2556
|
+
} catch {
|
|
2557
|
+
}
|
|
2558
|
+
}, [locked, onResend, prompt.userActionId, resendSec]);
|
|
2559
|
+
const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
|
|
2560
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.bodyWrap, children: [
|
|
2561
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2562
|
+
reactNative.ScrollView,
|
|
2563
|
+
{
|
|
2564
|
+
style: ub.fieldsScroll,
|
|
2565
|
+
contentContainerStyle: ub.fieldsContent,
|
|
2566
|
+
keyboardShouldPersistTaps: "handled",
|
|
2567
|
+
showsVerticalScrollIndicator: false,
|
|
2568
|
+
children: [
|
|
2569
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.desc, { color: pal.muted }], children: description }),
|
|
2570
|
+
isNumeric ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2571
|
+
CodeInput,
|
|
2572
|
+
{
|
|
2573
|
+
value: code,
|
|
2574
|
+
onChange: (v) => {
|
|
2575
|
+
setErrored(false);
|
|
2576
|
+
setCode(v);
|
|
2577
|
+
},
|
|
2578
|
+
length: codeLen,
|
|
2579
|
+
disabled: locked,
|
|
2580
|
+
error: errored,
|
|
2581
|
+
accent,
|
|
2582
|
+
pal
|
|
2583
|
+
}
|
|
2584
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2585
|
+
reactNative.TextInput,
|
|
2586
|
+
{
|
|
2587
|
+
value: code,
|
|
2588
|
+
onChangeText: (t) => {
|
|
2589
|
+
setErrored(false);
|
|
2590
|
+
setCode(t);
|
|
2591
|
+
},
|
|
2592
|
+
editable: !locked,
|
|
2593
|
+
placeholder: "Verification code",
|
|
2594
|
+
placeholderTextColor: pal.muted,
|
|
2595
|
+
autoComplete: "one-time-code",
|
|
2596
|
+
style: [
|
|
2597
|
+
ub.input,
|
|
2598
|
+
{ color: pal.text, backgroundColor: pal.fieldBg, borderColor: errored ? "#ef4444" : pal.fieldBorder }
|
|
2599
|
+
]
|
|
2600
|
+
}
|
|
2601
|
+
)
|
|
2602
|
+
]
|
|
2603
|
+
}
|
|
2604
|
+
),
|
|
2605
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.actions, children: [
|
|
2606
|
+
!isNumeric && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2607
|
+
PrimaryButton,
|
|
2608
|
+
{
|
|
2609
|
+
label: busy ? "Verifying\u2026" : "Verify",
|
|
2610
|
+
onPress: () => doSubmit(code.trim()),
|
|
2611
|
+
disabled: locked || !code.trim(),
|
|
2612
|
+
accent
|
|
2613
|
+
}
|
|
2614
|
+
),
|
|
2615
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.linkRow, children: [
|
|
2616
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { disabled: locked || resendSec > 0, onPress: () => void handleResend(), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.link, { color: accent, opacity: locked || resendSec > 0 ? 0.4 : 1 }], children: resendSec > 0 ? `Resend (${resendSec}s)` : "Resend" }) }),
|
|
2617
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { disabled: busy, onPress: () => void onCancel(prompt.userActionId), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.link, ub.linkDanger, { opacity: busy ? 0.4 : 1 }], children: "Cancel" }) })
|
|
2618
|
+
] })
|
|
2619
|
+
] })
|
|
2620
|
+
] });
|
|
2212
2621
|
}
|
|
2622
|
+
function FormBody({
|
|
2623
|
+
prompt,
|
|
2624
|
+
pal,
|
|
2625
|
+
accent,
|
|
2626
|
+
isDark,
|
|
2627
|
+
onSubmit,
|
|
2628
|
+
onCancel
|
|
2629
|
+
}) {
|
|
2630
|
+
const schema = prompt.requestedSchema;
|
|
2631
|
+
const fields = react.useMemo(() => renderableFields(schema), [schema]);
|
|
2632
|
+
const [values, setValues] = react.useState(() => {
|
|
2633
|
+
const init = {};
|
|
2634
|
+
for (const [key, field] of fields) init[key] = defaultValueFor(field);
|
|
2635
|
+
return init;
|
|
2636
|
+
});
|
|
2637
|
+
const [errors, setErrors] = react.useState({});
|
|
2638
|
+
const busy = prompt.status === "submitting";
|
|
2639
|
+
const stale = prompt.status === "stale";
|
|
2640
|
+
const locked = busy || stale;
|
|
2641
|
+
const isConfirm = prompt.subAction === "UserConfirmation";
|
|
2642
|
+
const setValue = (key, v) => {
|
|
2643
|
+
setValues((prev) => ({ ...prev, [key]: v }));
|
|
2644
|
+
setErrors((prev) => {
|
|
2645
|
+
if (!prev[key]) return prev;
|
|
2646
|
+
const next = { ...prev };
|
|
2647
|
+
delete next[key];
|
|
2648
|
+
return next;
|
|
2649
|
+
});
|
|
2650
|
+
};
|
|
2651
|
+
const handleSubmit = () => {
|
|
2652
|
+
if (locked) return;
|
|
2653
|
+
const validation = validateForm(schema, values);
|
|
2654
|
+
if (Object.keys(validation).length > 0) {
|
|
2655
|
+
setErrors(validation);
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
void onSubmit(prompt.userActionId, buildContent(schema, values)).catch(() => {
|
|
2659
|
+
});
|
|
2660
|
+
};
|
|
2661
|
+
const mdStyles = isDark ? MD_STYLES_DARK : MD_STYLES_LIGHT;
|
|
2662
|
+
const mdRules = react.useMemo(() => makeMdRules(isDark, accent), [isDark, accent]);
|
|
2663
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.bodyWrap, children: [
|
|
2664
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2665
|
+
reactNative.ScrollView,
|
|
2666
|
+
{
|
|
2667
|
+
style: ub.fieldsScroll,
|
|
2668
|
+
contentContainerStyle: ub.fieldsContent,
|
|
2669
|
+
keyboardShouldPersistTaps: "handled",
|
|
2670
|
+
showsVerticalScrollIndicator: true,
|
|
2671
|
+
children: [
|
|
2672
|
+
prompt.message?.trim() ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.messageBlock, children: /* @__PURE__ */ jsxRuntime.jsx(Markdown__default.default, { style: mdStyles, rules: mdRules, children: prompt.message.replace(/\\n/g, "\n") }) }) : null,
|
|
2673
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.form, children: fields.map(([key, field]) => {
|
|
2674
|
+
const widget = classifyField(field);
|
|
2675
|
+
const label = field.title || key;
|
|
2676
|
+
const required = isRequired(schema, key);
|
|
2677
|
+
const err = errors[key];
|
|
2678
|
+
if (widget === "boolean") {
|
|
2679
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.switchRow, children: [
|
|
2680
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [ub.label, { color: pal.text, flex: 1 }], children: [
|
|
2681
|
+
label,
|
|
2682
|
+
required ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: "#ef4444" }, children: " *" }) : null
|
|
2683
|
+
] }),
|
|
2684
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2685
|
+
reactNative.Switch,
|
|
2686
|
+
{
|
|
2687
|
+
value: Boolean(values[key]),
|
|
2688
|
+
disabled: locked,
|
|
2689
|
+
onValueChange: (v) => setValue(key, v),
|
|
2690
|
+
trackColor: { true: accent }
|
|
2691
|
+
}
|
|
2692
|
+
)
|
|
2693
|
+
] }, key);
|
|
2694
|
+
}
|
|
2695
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.field, children: [
|
|
2696
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.Text, { style: [ub.label, { color: pal.text }], children: [
|
|
2697
|
+
label,
|
|
2698
|
+
required ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: "#ef4444" }, children: " *" }) : null
|
|
2699
|
+
] }),
|
|
2700
|
+
field.description ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.hint, { color: pal.muted }], children: field.description }) : null,
|
|
2701
|
+
widget === "select" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.optionList, children: getOptions(field).map((opt) => {
|
|
2702
|
+
const selected = String(values[key] ?? "") === opt.const;
|
|
2703
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2704
|
+
reactNative.Pressable,
|
|
2705
|
+
{
|
|
2706
|
+
disabled: locked,
|
|
2707
|
+
onPress: () => setValue(key, opt.const),
|
|
2708
|
+
android_ripple: { color: accent + "22" },
|
|
2709
|
+
style: [
|
|
2710
|
+
ub.optionRow,
|
|
2711
|
+
{
|
|
2712
|
+
borderColor: selected ? accent : pal.fieldBorder,
|
|
2713
|
+
backgroundColor: selected ? accent + (isDark ? "26" : "14") : pal.fieldBg
|
|
2714
|
+
}
|
|
2715
|
+
],
|
|
2716
|
+
children: [
|
|
2717
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2718
|
+
reactNative.View,
|
|
2719
|
+
{
|
|
2720
|
+
style: [
|
|
2721
|
+
ub.radio,
|
|
2722
|
+
{ borderColor: selected ? accent : pal.fieldBorder }
|
|
2723
|
+
],
|
|
2724
|
+
children: selected ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [ub.radioDot, { backgroundColor: accent }] }) : null
|
|
2725
|
+
}
|
|
2726
|
+
),
|
|
2727
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2728
|
+
reactNative.Text,
|
|
2729
|
+
{
|
|
2730
|
+
style: [
|
|
2731
|
+
ub.optionLabel,
|
|
2732
|
+
{ color: pal.text, fontWeight: selected ? "600" : "500" }
|
|
2733
|
+
],
|
|
2734
|
+
children: opt.title || opt.const
|
|
2735
|
+
}
|
|
2736
|
+
)
|
|
2737
|
+
]
|
|
2738
|
+
},
|
|
2739
|
+
opt.const
|
|
2740
|
+
);
|
|
2741
|
+
}) }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2742
|
+
reactNative.TextInput,
|
|
2743
|
+
{
|
|
2744
|
+
value: String(values[key] ?? ""),
|
|
2745
|
+
editable: !locked,
|
|
2746
|
+
onChangeText: (t) => setValue(key, t),
|
|
2747
|
+
placeholder: field.description ? void 0 : label,
|
|
2748
|
+
placeholderTextColor: pal.muted,
|
|
2749
|
+
keyboardType: widget === "integer" ? "number-pad" : widget === "decimal" ? "decimal-pad" : "default",
|
|
2750
|
+
style: [
|
|
2751
|
+
ub.input,
|
|
2752
|
+
{ color: pal.text, backgroundColor: pal.fieldBg, borderColor: err ? "#ef4444" : pal.fieldBorder }
|
|
2753
|
+
]
|
|
2754
|
+
}
|
|
2755
|
+
),
|
|
2756
|
+
err ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: ub.error, children: err }) : null
|
|
2757
|
+
] }, key);
|
|
2758
|
+
}) })
|
|
2759
|
+
]
|
|
2760
|
+
}
|
|
2761
|
+
),
|
|
2762
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: ub.actions, children: [
|
|
2763
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2764
|
+
PrimaryButton,
|
|
2765
|
+
{
|
|
2766
|
+
label: busy ? "Submitting\u2026" : isConfirm ? "Confirm" : "Next",
|
|
2767
|
+
onPress: handleSubmit,
|
|
2768
|
+
disabled: locked,
|
|
2769
|
+
accent
|
|
2770
|
+
}
|
|
2771
|
+
),
|
|
2772
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.linkRow, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { disabled: busy, onPress: () => void onCancel(prompt.userActionId), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.link, ub.linkDanger, { opacity: busy ? 0.4 : 1 }], children: "Cancel" }) }) })
|
|
2773
|
+
] })
|
|
2774
|
+
] });
|
|
2775
|
+
}
|
|
2776
|
+
function UserActionSheet({
|
|
2777
|
+
open,
|
|
2778
|
+
active,
|
|
2779
|
+
expired,
|
|
2780
|
+
isDark,
|
|
2781
|
+
accent,
|
|
2782
|
+
insetBottom,
|
|
2783
|
+
onSubmit,
|
|
2784
|
+
onCancel,
|
|
2785
|
+
onResend,
|
|
2786
|
+
onMinimize
|
|
2787
|
+
}) {
|
|
2788
|
+
const pal = uaPalette(isDark);
|
|
2789
|
+
const screenH = reactNative.Dimensions.get("window").height;
|
|
2790
|
+
const [shown, setShown] = react.useState(active);
|
|
2791
|
+
react.useEffect(() => {
|
|
2792
|
+
if (active) setShown(active);
|
|
2793
|
+
}, [active]);
|
|
2794
|
+
const translateY = react.useRef(new reactNative.Animated.Value(screenH)).current;
|
|
2795
|
+
const backdrop = react.useRef(new reactNative.Animated.Value(0)).current;
|
|
2796
|
+
const minimizeRef = react.useRef(onMinimize);
|
|
2797
|
+
react.useEffect(() => {
|
|
2798
|
+
minimizeRef.current = onMinimize;
|
|
2799
|
+
});
|
|
2800
|
+
const [kbHeight, setKbHeight] = react.useState(0);
|
|
2801
|
+
react.useEffect(() => {
|
|
2802
|
+
const showEvt = reactNative.Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow";
|
|
2803
|
+
const hideEvt = reactNative.Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide";
|
|
2804
|
+
const s1 = reactNative.Keyboard.addListener(showEvt, (e) => setKbHeight(e?.endCoordinates?.height ?? 0));
|
|
2805
|
+
const s2 = reactNative.Keyboard.addListener(hideEvt, () => setKbHeight(0));
|
|
2806
|
+
return () => {
|
|
2807
|
+
s1.remove();
|
|
2808
|
+
s2.remove();
|
|
2809
|
+
};
|
|
2810
|
+
}, []);
|
|
2811
|
+
react.useEffect(() => {
|
|
2812
|
+
if (open) {
|
|
2813
|
+
reactNative.Animated.parallel([
|
|
2814
|
+
reactNative.Animated.spring(translateY, { toValue: 0, useNativeDriver: true, damping: 30, stiffness: 360, mass: 0.85 }),
|
|
2815
|
+
reactNative.Animated.timing(backdrop, { toValue: 1, duration: 190, useNativeDriver: true })
|
|
2816
|
+
]).start();
|
|
2817
|
+
} else {
|
|
2818
|
+
reactNative.Animated.parallel([
|
|
2819
|
+
reactNative.Animated.timing(translateY, { toValue: screenH, duration: 210, easing: reactNative.Easing.in(reactNative.Easing.cubic), useNativeDriver: true }),
|
|
2820
|
+
reactNative.Animated.timing(backdrop, { toValue: 0, duration: 170, useNativeDriver: true })
|
|
2821
|
+
]).start();
|
|
2822
|
+
}
|
|
2823
|
+
}, [open, translateY, backdrop, screenH]);
|
|
2824
|
+
const pan = react.useRef(
|
|
2825
|
+
reactNative.PanResponder.create({
|
|
2826
|
+
onMoveShouldSetPanResponder: (_evt, g) => g.dy > 6 && Math.abs(g.dy) > Math.abs(g.dx) * 1.4,
|
|
2827
|
+
onPanResponderMove: (_evt, g) => {
|
|
2828
|
+
if (g.dy > 0) translateY.setValue(g.dy);
|
|
2829
|
+
},
|
|
2830
|
+
onPanResponderRelease: (_evt, g) => {
|
|
2831
|
+
if (g.dy > 110 || g.vy > 0.8) {
|
|
2832
|
+
minimizeRef.current();
|
|
2833
|
+
} else {
|
|
2834
|
+
reactNative.Animated.spring(translateY, { toValue: 0, useNativeDriver: true, damping: 30, stiffness: 360 }).start();
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
})
|
|
2838
|
+
).current;
|
|
2839
|
+
const a = active ?? shown;
|
|
2840
|
+
if (!a) return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { pointerEvents: "none" });
|
|
2841
|
+
const isVerification = a.kind === "verification";
|
|
2842
|
+
const title = isVerification ? "Verification required" : "Action required";
|
|
2843
|
+
const subtitle = isVerification ? "Confirm the one-time code to continue" : "Review the details and confirm to continue";
|
|
2844
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [reactNative.StyleSheet.absoluteFill, sht.overlay], pointerEvents: open ? "auto" : "none", children: [
|
|
2845
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2846
|
+
reactNative.Animated.View,
|
|
2847
|
+
{
|
|
2848
|
+
pointerEvents: "none",
|
|
2849
|
+
style: [sht.backdrop, { backgroundColor: pal.overlay, opacity: backdrop }]
|
|
2850
|
+
}
|
|
2851
|
+
),
|
|
2852
|
+
open ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { style: reactNative.StyleSheet.absoluteFill, onPress: () => minimizeRef.current(), accessibilityLabel: "Dismiss" }) : null,
|
|
2853
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [sht.kav, { paddingBottom: kbHeight }], pointerEvents: "box-none", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2854
|
+
reactNative.Animated.View,
|
|
2855
|
+
{
|
|
2856
|
+
style: [
|
|
2857
|
+
sht.sheet,
|
|
2858
|
+
{
|
|
2859
|
+
backgroundColor: pal.sheetBg,
|
|
2860
|
+
borderColor: pal.border,
|
|
2861
|
+
// Cap to the space above the keyboard (and a top margin) so the
|
|
2862
|
+
// header stays visible and the footer sits above the keyboard;
|
|
2863
|
+
// the field area scrolls within whatever's left.
|
|
2864
|
+
maxHeight: Math.min(screenH * 0.9, screenH - kbHeight - 72),
|
|
2865
|
+
paddingBottom: kbHeight > 0 ? 16 : Math.max(insetBottom, 16) + 6,
|
|
2866
|
+
transform: [{ translateY }]
|
|
2867
|
+
}
|
|
2868
|
+
],
|
|
2869
|
+
children: [
|
|
2870
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ...pan.panHandlers, style: sht.handleZone, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [sht.grabber, { backgroundColor: pal.grabber }] }) }),
|
|
2871
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: sht.header, children: [
|
|
2872
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [sht.iconCircle, { backgroundColor: accent + (isDark ? "26" : "14") }], children: isVerification ? /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ShieldCheck, { size: 20, color: accent, strokeWidth: 2 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Pencil, { size: 18, color: accent, strokeWidth: 2 }) }),
|
|
2873
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: sht.headerText, children: [
|
|
2874
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [sht.title, { color: pal.text }], children: title }),
|
|
2875
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [sht.subtitle, { color: pal.muted }], numberOfLines: 1, children: subtitle })
|
|
2876
|
+
] }),
|
|
2877
|
+
/* @__PURE__ */ jsxRuntime.jsx(SheetTimerPill, { prompt: a, accent, isDark }),
|
|
2878
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => minimizeRef.current(), hitSlop: 10, style: [sht.closeBtn, { backgroundColor: pal.fieldBg }], children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 16, color: pal.muted, strokeWidth: 2.2 }) })
|
|
2879
|
+
] }),
|
|
2880
|
+
a.status === "stale" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: sht.simpleBody, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.desc, { color: pal.muted }], children: "This request is no longer available." }) }) : expired ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: sht.simpleBody, children: [
|
|
2881
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.desc, { color: pal.muted }], children: isVerification ? "This verification request expired." : "This request expired." }),
|
|
2882
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.actions, children: /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { label: "Close", onPress: () => void onCancel(a.userActionId), accent }) })
|
|
2883
|
+
] }) : isVerification ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2884
|
+
VerificationBody,
|
|
2885
|
+
{
|
|
2886
|
+
prompt: a,
|
|
2887
|
+
expired,
|
|
2888
|
+
pal,
|
|
2889
|
+
accent,
|
|
2890
|
+
onSubmit,
|
|
2891
|
+
onCancel,
|
|
2892
|
+
onResend
|
|
2893
|
+
},
|
|
2894
|
+
a.userActionId
|
|
2895
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(FormBody, { prompt: a, pal, accent, isDark, onSubmit, onCancel }, a.userActionId)
|
|
2896
|
+
]
|
|
2897
|
+
}
|
|
2898
|
+
) })
|
|
2899
|
+
] });
|
|
2900
|
+
}
|
|
2901
|
+
function UserActionReopenCard({
|
|
2902
|
+
active,
|
|
2903
|
+
expired,
|
|
2904
|
+
isDark,
|
|
2905
|
+
accent,
|
|
2906
|
+
onReopen
|
|
2907
|
+
}) {
|
|
2908
|
+
if (expired || active.status === "stale") return null;
|
|
2909
|
+
const pal = uaPalette(isDark);
|
|
2910
|
+
const isVerification = active.kind === "verification";
|
|
2911
|
+
const title = isVerification ? "Verification required" : "Action required";
|
|
2912
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2913
|
+
reactNative.Pressable,
|
|
2914
|
+
{
|
|
2915
|
+
onPress: onReopen,
|
|
2916
|
+
accessibilityRole: "button",
|
|
2917
|
+
accessibilityLabel: `${title}. Tap to respond.`,
|
|
2918
|
+
android_ripple: { color: accent + "22" },
|
|
2919
|
+
style: [
|
|
2920
|
+
rc.card,
|
|
2921
|
+
{
|
|
2922
|
+
backgroundColor: pal.sheetBg,
|
|
2923
|
+
borderColor: accent + (isDark ? "59" : "40")
|
|
2924
|
+
}
|
|
2925
|
+
],
|
|
2926
|
+
children: [
|
|
2927
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [rc.iconCircle, { backgroundColor: accent + (isDark ? "26" : "14") }], children: isVerification ? /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ShieldCheck, { size: 18, color: accent, strokeWidth: 2 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Pencil, { size: 16, color: accent, strokeWidth: 2 }) }),
|
|
2928
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: rc.textWrap, children: [
|
|
2929
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [rc.title, { color: pal.text }], numberOfLines: 1, children: title }),
|
|
2930
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [rc.subtitle, { color: pal.muted }], numberOfLines: 1, children: "Tap to respond and continue" })
|
|
2931
|
+
] }),
|
|
2932
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTimerPill, { prompt: active, accent, isDark }),
|
|
2933
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.ChevronRight, { size: 18, color: pal.muted, strokeWidth: 2 })
|
|
2934
|
+
]
|
|
2935
|
+
}
|
|
2936
|
+
);
|
|
2937
|
+
}
|
|
2938
|
+
function NotificationStack({
|
|
2939
|
+
notifications,
|
|
2940
|
+
isDark,
|
|
2941
|
+
onDismiss
|
|
2942
|
+
}) {
|
|
2943
|
+
if (notifications.length === 0) return null;
|
|
2944
|
+
const pal = uaPalette(isDark);
|
|
2945
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: ub.noteStack, children: notifications.map((n) => /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [ub.note, { backgroundColor: pal.fieldBg, borderColor: pal.border }], children: [
|
|
2946
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.Info, { size: 14, color: pal.muted, strokeWidth: 1.9 }),
|
|
2947
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [ub.noteText, { color: pal.text }], children: n.message }),
|
|
2948
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => onDismiss(n.id), hitSlop: 8, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReactNative.X, { size: 14, color: pal.muted, strokeWidth: 2 }) })
|
|
2949
|
+
] }, n.id)) });
|
|
2950
|
+
}
|
|
2951
|
+
var ub = reactNative.StyleSheet.create({
|
|
2952
|
+
// Body wrapper can shrink within the sheet's (bounded) maxHeight so the inner
|
|
2953
|
+
// field scroll takes over for tall forms while the footer stays pinned.
|
|
2954
|
+
bodyWrap: { flexShrink: 1 },
|
|
2955
|
+
// Field scroll region. flexShrink:1 + no flexGrow → hugs content for short
|
|
2956
|
+
// forms (no scroll), shrinks and scrolls only when the form exceeds the sheet.
|
|
2957
|
+
fieldsScroll: { flexShrink: 1 },
|
|
2958
|
+
fieldsContent: { paddingBottom: 6 },
|
|
2959
|
+
messageBlock: { marginBottom: 18, marginTop: 2 },
|
|
2960
|
+
desc: { fontSize: 14.5, lineHeight: 21, marginBottom: 16 },
|
|
2961
|
+
form: { gap: 18 },
|
|
2962
|
+
field: { gap: 7 },
|
|
2963
|
+
label: { fontSize: 14, fontWeight: "600" },
|
|
2964
|
+
hint: { fontSize: 12.5, lineHeight: 17 },
|
|
2965
|
+
input: {
|
|
2966
|
+
borderWidth: 1,
|
|
2967
|
+
borderRadius: 12,
|
|
2968
|
+
paddingHorizontal: 14,
|
|
2969
|
+
paddingVertical: 13,
|
|
2970
|
+
fontSize: 16
|
|
2971
|
+
},
|
|
2972
|
+
error: { color: "#ef4444", fontSize: 12.5, marginTop: 2 },
|
|
2973
|
+
switchRow: { flexDirection: "row", alignItems: "center", gap: 12 },
|
|
2974
|
+
// Selectable option "boxes" for oneOf / enum single-select fields.
|
|
2975
|
+
optionList: { gap: 10 },
|
|
2976
|
+
optionRow: {
|
|
2977
|
+
flexDirection: "row",
|
|
2978
|
+
alignItems: "center",
|
|
2979
|
+
gap: 12,
|
|
2980
|
+
borderWidth: 1.5,
|
|
2981
|
+
borderRadius: 14,
|
|
2982
|
+
paddingHorizontal: 14,
|
|
2983
|
+
paddingVertical: 14
|
|
2984
|
+
},
|
|
2985
|
+
radio: {
|
|
2986
|
+
width: 20,
|
|
2987
|
+
height: 20,
|
|
2988
|
+
borderRadius: 10,
|
|
2989
|
+
borderWidth: 2,
|
|
2990
|
+
alignItems: "center",
|
|
2991
|
+
justifyContent: "center"
|
|
2992
|
+
},
|
|
2993
|
+
radioDot: { width: 10, height: 10, borderRadius: 5 },
|
|
2994
|
+
optionLabel: { flex: 1, fontSize: 15, letterSpacing: -0.1 },
|
|
2995
|
+
// Pinned footer (outside the fields scroll) — always visible.
|
|
2996
|
+
actions: { paddingTop: 16, gap: 14 },
|
|
2997
|
+
primaryBtn: { minHeight: 52, borderRadius: 14, borderWidth: 1, paddingVertical: 15, alignItems: "center", justifyContent: "center" },
|
|
2998
|
+
primaryBtnText: { color: "#fff", fontSize: 15.5, fontWeight: "600", letterSpacing: -0.1 },
|
|
2999
|
+
linkRow: { flexDirection: "row", justifyContent: "center", gap: 24 },
|
|
3000
|
+
link: { fontSize: 14, fontWeight: "500" },
|
|
3001
|
+
linkDanger: { color: "#dc2626" },
|
|
3002
|
+
codeRow: { flexDirection: "row", justifyContent: "center", gap: 9, position: "relative" },
|
|
3003
|
+
codeBox: {
|
|
3004
|
+
width: 46,
|
|
3005
|
+
height: 56,
|
|
3006
|
+
borderWidth: 1.5,
|
|
3007
|
+
borderRadius: 12,
|
|
3008
|
+
alignItems: "center",
|
|
3009
|
+
justifyContent: "center"
|
|
3010
|
+
},
|
|
3011
|
+
codeChar: { fontSize: 24, fontWeight: "600" },
|
|
3012
|
+
codeHidden: { ...reactNative.StyleSheet.absoluteFillObject, opacity: 0 },
|
|
3013
|
+
noteStack: { paddingHorizontal: 16, paddingBottom: 6, gap: 6 },
|
|
3014
|
+
note: {
|
|
3015
|
+
flexDirection: "row",
|
|
3016
|
+
alignItems: "center",
|
|
3017
|
+
gap: 8,
|
|
3018
|
+
borderWidth: 1,
|
|
3019
|
+
borderRadius: 12,
|
|
3020
|
+
paddingHorizontal: 12,
|
|
3021
|
+
paddingVertical: 10
|
|
3022
|
+
},
|
|
3023
|
+
noteText: { flex: 1, fontSize: 13.5, lineHeight: 19 }
|
|
3024
|
+
});
|
|
3025
|
+
var sht = reactNative.StyleSheet.create({
|
|
3026
|
+
// High stacking so the sheet sits above the chat input bar (which carries its
|
|
3027
|
+
// own zIndex/elevation) and everything else in the chat.
|
|
3028
|
+
overlay: { zIndex: 100, ...reactNative.Platform.select({ android: { elevation: 100 }, default: {} }) },
|
|
3029
|
+
backdrop: { ...reactNative.StyleSheet.absoluteFillObject },
|
|
3030
|
+
kav: { flex: 1, justifyContent: "flex-end" },
|
|
3031
|
+
sheet: {
|
|
3032
|
+
borderTopLeftRadius: 28,
|
|
3033
|
+
borderTopRightRadius: 28,
|
|
3034
|
+
borderWidth: reactNative.StyleSheet.hairlineWidth,
|
|
3035
|
+
borderBottomWidth: 0,
|
|
3036
|
+
paddingHorizontal: 22,
|
|
3037
|
+
paddingTop: 6,
|
|
3038
|
+
maxWidth: 640,
|
|
3039
|
+
width: "100%",
|
|
3040
|
+
alignSelf: "center",
|
|
3041
|
+
...reactNative.Platform.select({
|
|
3042
|
+
ios: {
|
|
3043
|
+
shadowColor: "#000",
|
|
3044
|
+
shadowOffset: { width: 0, height: -6 },
|
|
3045
|
+
shadowOpacity: 0.18,
|
|
3046
|
+
shadowRadius: 24
|
|
3047
|
+
},
|
|
3048
|
+
android: { elevation: 24 }
|
|
3049
|
+
})
|
|
3050
|
+
},
|
|
3051
|
+
handleZone: { alignItems: "center", paddingVertical: 10 },
|
|
3052
|
+
grabber: { width: 40, height: 5, borderRadius: 3 },
|
|
3053
|
+
header: { flexDirection: "row", alignItems: "center", gap: 12, marginTop: 2, marginBottom: 18 },
|
|
3054
|
+
iconCircle: { width: 40, height: 40, borderRadius: 20, alignItems: "center", justifyContent: "center" },
|
|
3055
|
+
headerText: { flex: 1, gap: 2 },
|
|
3056
|
+
title: { fontSize: 18, fontWeight: "700", letterSpacing: -0.3 },
|
|
3057
|
+
subtitle: { fontSize: 13, letterSpacing: -0.1 },
|
|
3058
|
+
timer: { borderWidth: 1, borderRadius: 999, paddingHorizontal: 11, paddingVertical: 5, minWidth: 46, alignItems: "center" },
|
|
3059
|
+
closeBtn: { width: 30, height: 30, borderRadius: 15, alignItems: "center", justifyContent: "center" },
|
|
3060
|
+
// Short, non-scrolling bodies (stale / expired notices).
|
|
3061
|
+
simpleBody: { paddingBottom: 8 }
|
|
3062
|
+
});
|
|
3063
|
+
var rc = reactNative.StyleSheet.create({
|
|
3064
|
+
card: {
|
|
3065
|
+
flexDirection: "row",
|
|
3066
|
+
alignItems: "center",
|
|
3067
|
+
gap: 12,
|
|
3068
|
+
borderWidth: 1.5,
|
|
3069
|
+
borderRadius: 18,
|
|
3070
|
+
paddingHorizontal: 14,
|
|
3071
|
+
paddingVertical: 13,
|
|
3072
|
+
marginVertical: 6
|
|
3073
|
+
},
|
|
3074
|
+
iconCircle: { width: 34, height: 34, borderRadius: 17, alignItems: "center", justifyContent: "center" },
|
|
3075
|
+
textWrap: { flex: 1, gap: 1 },
|
|
3076
|
+
title: { fontSize: 14.5, fontWeight: "700", letterSpacing: -0.2 },
|
|
3077
|
+
subtitle: { fontSize: 12.5, letterSpacing: -0.1 },
|
|
3078
|
+
timer: { borderRadius: 999, paddingHorizontal: 9, paddingVertical: 4, minWidth: 40, alignItems: "center" }
|
|
3079
|
+
});
|
|
2213
3080
|
var PaymanChat = react.forwardRef(
|
|
2214
3081
|
function PaymanChat2({ config, callbacks, children, onLoadMoreMessages, isLoadingMoreMessages = false, hasMoreMessages = false }, ref) {
|
|
2215
3082
|
const accent = config.accent ?? DEFAULT_ACCENT;
|
|
@@ -2236,8 +3103,14 @@ var PaymanChat = react.forwardRef(
|
|
|
2236
3103
|
},
|
|
2237
3104
|
onStatusMessage: (m) => callbacksRef.current?.onStatusMessage?.(m),
|
|
2238
3105
|
onStepsUpdate: (steps) => callbacksRef.current?.onStepsUpdate?.(steps),
|
|
2239
|
-
onUserActionRequired: (r) =>
|
|
2240
|
-
|
|
3106
|
+
onUserActionRequired: (r) => {
|
|
3107
|
+
console.log("[PaymanChat] USER_ACTION_REQUIRED:", JSON.stringify({ kind: r?.kind, rawAction: r?.rawAction, userActionId: r?.userActionId, hasSchema: !!r?.requestedSchema }));
|
|
3108
|
+
callbacksRef.current?.onUserActionRequired?.(r);
|
|
3109
|
+
},
|
|
3110
|
+
onUserNotification: (n) => {
|
|
3111
|
+
console.log("[PaymanChat] USER_NOTIFICATION:", JSON.stringify({ id: n?.id, message: n?.message }));
|
|
3112
|
+
callbacksRef.current?.onUserNotification?.(n);
|
|
3113
|
+
}
|
|
2241
3114
|
}), []);
|
|
2242
3115
|
const {
|
|
2243
3116
|
messages,
|
|
@@ -2248,8 +3121,37 @@ var PaymanChat = react.forwardRef(
|
|
|
2248
3121
|
prependMessages,
|
|
2249
3122
|
cancelStream,
|
|
2250
3123
|
getSessionId,
|
|
2251
|
-
getMessages
|
|
3124
|
+
getMessages,
|
|
3125
|
+
userActionState,
|
|
3126
|
+
submitUserAction: submitUserAction2,
|
|
3127
|
+
cancelUserAction: cancelUserAction2,
|
|
3128
|
+
resendUserAction: resendUserAction2,
|
|
3129
|
+
dismissNotification
|
|
2252
3130
|
} = useChatV2(config, stableCallbacks);
|
|
3131
|
+
react.useEffect(() => {
|
|
3132
|
+
console.log(
|
|
3133
|
+
"[PaymanChat] userActionState ->",
|
|
3134
|
+
"prompts:",
|
|
3135
|
+
userActionState.prompts.length,
|
|
3136
|
+
userActionState.prompts.map((p) => `${p.kind}:${p.status}`).join(","),
|
|
3137
|
+
"| notifications:",
|
|
3138
|
+
userActionState.notifications.length
|
|
3139
|
+
);
|
|
3140
|
+
}, [userActionState]);
|
|
3141
|
+
const activePrompt = react.useMemo(
|
|
3142
|
+
() => userActionState.prompts.find((p) => p.kind !== "notification"),
|
|
3143
|
+
[userActionState.prompts]
|
|
3144
|
+
);
|
|
3145
|
+
const expired = useExpiredFlag(activePrompt);
|
|
3146
|
+
const [dismissedActionId, setDismissedActionId] = react.useState(null);
|
|
3147
|
+
react.useEffect(() => {
|
|
3148
|
+
if (activePrompt) setDismissedActionId(null);
|
|
3149
|
+
}, [activePrompt?.userActionId, activePrompt?.subAction]);
|
|
3150
|
+
const sheetOpen = !!activePrompt && dismissedActionId !== activePrompt.userActionId;
|
|
3151
|
+
const showReopenCard = !!activePrompt && !sheetOpen && !expired && activePrompt.status !== "stale";
|
|
3152
|
+
const minimizeActivePrompt = react.useCallback(() => {
|
|
3153
|
+
setDismissedActionId((prev) => activePrompt?.userActionId ?? prev);
|
|
3154
|
+
}, [activePrompt?.userActionId]);
|
|
2253
3155
|
const {
|
|
2254
3156
|
transcribedText,
|
|
2255
3157
|
isAvailable: voiceAvailable,
|
|
@@ -2326,7 +3228,7 @@ var PaymanChat = react.forwardRef(
|
|
|
2326
3228
|
stopRecording();
|
|
2327
3229
|
clearTranscript();
|
|
2328
3230
|
}, [stopRecording, clearTranscript]);
|
|
2329
|
-
const isInputDisabled =
|
|
3231
|
+
const isInputDisabled = !config.sessionParams?.id?.trim();
|
|
2330
3232
|
const enableVoice = config.enableVoice !== false;
|
|
2331
3233
|
const isEmpty = messages.length === 0;
|
|
2332
3234
|
const prevMessageCountRef = react.useRef(messages.length);
|
|
@@ -2376,14 +3278,33 @@ var PaymanChat = react.forwardRef(
|
|
|
2376
3278
|
message: msg,
|
|
2377
3279
|
accent,
|
|
2378
3280
|
shouldType: typingMessageIds.current.has(msg.id),
|
|
2379
|
-
isDark
|
|
3281
|
+
isDark,
|
|
3282
|
+
suppressText: activePrompt?.message && msg.role === "assistant" && msg.id === messages[messages.length - 1]?.id ? activePrompt.message : void 0
|
|
2380
3283
|
},
|
|
2381
3284
|
msg.id
|
|
2382
3285
|
)),
|
|
3286
|
+
showReopenCard && activePrompt ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3287
|
+
UserActionReopenCard,
|
|
3288
|
+
{
|
|
3289
|
+
active: activePrompt,
|
|
3290
|
+
expired,
|
|
3291
|
+
isDark,
|
|
3292
|
+
accent,
|
|
3293
|
+
onReopen: () => setDismissedActionId(null)
|
|
3294
|
+
}
|
|
3295
|
+
) : null,
|
|
2383
3296
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { height: 4 } })
|
|
2384
3297
|
]
|
|
2385
3298
|
}
|
|
2386
3299
|
) }),
|
|
3300
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3301
|
+
NotificationStack,
|
|
3302
|
+
{
|
|
3303
|
+
notifications: userActionState.notifications,
|
|
3304
|
+
isDark,
|
|
3305
|
+
onDismiss: dismissNotification
|
|
3306
|
+
}
|
|
3307
|
+
),
|
|
2387
3308
|
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: s.inputBarWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2388
3309
|
InputBar,
|
|
2389
3310
|
{
|
|
@@ -2407,7 +3328,22 @@ var PaymanChat = react.forwardRef(
|
|
|
2407
3328
|
},
|
|
2408
3329
|
insetBottom: config.contentInsetBottom ?? (reactNative.Platform.OS === "ios" ? 20 : 8)
|
|
2409
3330
|
}
|
|
2410
|
-
) })
|
|
3331
|
+
) }),
|
|
3332
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3333
|
+
UserActionSheet,
|
|
3334
|
+
{
|
|
3335
|
+
open: sheetOpen,
|
|
3336
|
+
active: activePrompt,
|
|
3337
|
+
expired,
|
|
3338
|
+
isDark,
|
|
3339
|
+
accent,
|
|
3340
|
+
insetBottom: config.contentInsetBottom ?? (reactNative.Platform.OS === "ios" ? 20 : 8),
|
|
3341
|
+
onSubmit: submitUserAction2,
|
|
3342
|
+
onCancel: cancelUserAction2,
|
|
3343
|
+
onResend: resendUserAction2,
|
|
3344
|
+
onMinimize: minimizeActivePrompt
|
|
3345
|
+
}
|
|
3346
|
+
)
|
|
2411
3347
|
]
|
|
2412
3348
|
}
|
|
2413
3349
|
) });
|