@vinaverse/react-native 0.1.0-alpha.2 → 0.1.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -2
- package/dist/index.cjs +457 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -47
- package/dist/index.d.ts +143 -47
- package/dist/index.js +448 -100
- package/dist/index.js.map +1 -1
- package/package.json +37 -5
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var flashList = require('@shopify/flash-list');
|
|
4
|
+
var react$1 = require('@vinaverse/react');
|
|
3
5
|
var reactNative = require('react-native');
|
|
4
6
|
var react = require('react');
|
|
5
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
8
|
var tokens = require('@vinaverse/tokens');
|
|
7
|
-
var react$1 = require('@vinaverse/react');
|
|
8
9
|
var conversation = require('@vinaverse/client/conversation');
|
|
9
|
-
var flashList = require('@shopify/flash-list');
|
|
10
10
|
|
|
11
|
-
// src/cards/
|
|
11
|
+
// src/cards/CardCarousel.tsx
|
|
12
12
|
var ChatContext = react.createContext(null);
|
|
13
13
|
function ChatContextProvider(props) {
|
|
14
14
|
return /* @__PURE__ */ jsxRuntime.jsx(ChatContext.Provider, { value: props.value, children: props.children });
|
|
@@ -57,6 +57,61 @@ function ThemeProvider(props) {
|
|
|
57
57
|
function useTheme() {
|
|
58
58
|
return react.useContext(ThemeContext);
|
|
59
59
|
}
|
|
60
|
+
var Button = react.memo(function Button2(props) {
|
|
61
|
+
const { colors: colors2, radius: radius2, space: space2, size: size3, font: font2 } = useTheme();
|
|
62
|
+
const variant = props.variant ?? "secondary";
|
|
63
|
+
const inactive = props.disabled === true || props.busy === true;
|
|
64
|
+
const filled = variant === "primary" || props.selected === true;
|
|
65
|
+
const background = filled ? colors2.primary : variant === "secondary" ? colors2.surface : "transparent";
|
|
66
|
+
const foreground = filled ? colors2.onPrimary : colors2.primary;
|
|
67
|
+
const checkable = props.role === "radio" || props.role === "checkbox";
|
|
68
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
69
|
+
reactNative.Pressable,
|
|
70
|
+
{
|
|
71
|
+
accessibilityRole: props.role ?? "button",
|
|
72
|
+
accessibilityLabel: props.label,
|
|
73
|
+
...props.hint ? { accessibilityHint: props.hint } : {},
|
|
74
|
+
accessibilityState: {
|
|
75
|
+
disabled: inactive,
|
|
76
|
+
busy: props.busy === true,
|
|
77
|
+
...checkable ? props.role === "radio" ? { selected: props.selected === true } : { checked: props.selected === true } : {}
|
|
78
|
+
},
|
|
79
|
+
disabled: inactive,
|
|
80
|
+
onPress: props.onPress,
|
|
81
|
+
...props.testID ? { testID: props.testID } : {},
|
|
82
|
+
style: ({ pressed }) => ({
|
|
83
|
+
minHeight: size3.touchTarget,
|
|
84
|
+
minWidth: size3.touchTarget,
|
|
85
|
+
paddingHorizontal: space2.md,
|
|
86
|
+
paddingVertical: space2.sm,
|
|
87
|
+
borderRadius: radius2.md,
|
|
88
|
+
borderWidth: variant === "text" ? 0 : 1,
|
|
89
|
+
borderColor: filled ? colors2.primary : colors2.border,
|
|
90
|
+
backgroundColor: background,
|
|
91
|
+
opacity: inactive ? 0.55 : pressed ? 0.8 : 1,
|
|
92
|
+
alignItems: "center",
|
|
93
|
+
justifyContent: "center",
|
|
94
|
+
flexDirection: "row",
|
|
95
|
+
gap: space2.xs
|
|
96
|
+
}),
|
|
97
|
+
children: [
|
|
98
|
+
props.busy ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: foreground }) : null,
|
|
99
|
+
props.children ?? /* @__PURE__ */ jsxRuntime.jsx(
|
|
100
|
+
reactNative.Text,
|
|
101
|
+
{
|
|
102
|
+
style: {
|
|
103
|
+
color: foreground,
|
|
104
|
+
fontSize: font2.size.md,
|
|
105
|
+
fontWeight: "500",
|
|
106
|
+
...font2.family ? { fontFamily: font2.family } : {}
|
|
107
|
+
},
|
|
108
|
+
children: props.label
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
});
|
|
60
115
|
function cardOf(held) {
|
|
61
116
|
return held.card;
|
|
62
117
|
}
|
|
@@ -125,64 +180,20 @@ function useLink(link, held) {
|
|
|
125
180
|
if (!link || !store.checkLink(link)) return null;
|
|
126
181
|
return () => handlers.current?.onOpenLink?.(link, held);
|
|
127
182
|
}
|
|
183
|
+
function ratingText(valuePpm, locale) {
|
|
184
|
+
const value = valuePpm / 1e6;
|
|
185
|
+
try {
|
|
186
|
+
return new Intl.NumberFormat(locale, {
|
|
187
|
+
minimumFractionDigits: 1,
|
|
188
|
+
maximumFractionDigits: 1
|
|
189
|
+
}).format(value);
|
|
190
|
+
} catch {
|
|
191
|
+
return value.toFixed(1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
128
194
|
function safeImage(url) {
|
|
129
195
|
return url && /^https:\/\//i.test(url) ? url : null;
|
|
130
196
|
}
|
|
131
|
-
var Button = react.memo(function Button2(props) {
|
|
132
|
-
const { colors: colors2, radius: radius2, space: space2, size: size3, font: font2 } = useTheme();
|
|
133
|
-
const variant = props.variant ?? "secondary";
|
|
134
|
-
const inactive = props.disabled === true || props.busy === true;
|
|
135
|
-
const filled = variant === "primary" || props.selected === true;
|
|
136
|
-
const background = filled ? colors2.primary : variant === "secondary" ? colors2.surface : "transparent";
|
|
137
|
-
const foreground = filled ? colors2.onPrimary : colors2.primary;
|
|
138
|
-
const checkable = props.role === "radio" || props.role === "checkbox";
|
|
139
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
140
|
-
reactNative.Pressable,
|
|
141
|
-
{
|
|
142
|
-
accessibilityRole: props.role ?? "button",
|
|
143
|
-
accessibilityLabel: props.label,
|
|
144
|
-
...props.hint ? { accessibilityHint: props.hint } : {},
|
|
145
|
-
accessibilityState: {
|
|
146
|
-
disabled: inactive,
|
|
147
|
-
busy: props.busy === true,
|
|
148
|
-
...checkable ? props.role === "radio" ? { selected: props.selected === true } : { checked: props.selected === true } : {}
|
|
149
|
-
},
|
|
150
|
-
disabled: inactive,
|
|
151
|
-
onPress: props.onPress,
|
|
152
|
-
...props.testID ? { testID: props.testID } : {},
|
|
153
|
-
style: ({ pressed }) => ({
|
|
154
|
-
minHeight: size3.touchTarget,
|
|
155
|
-
minWidth: size3.touchTarget,
|
|
156
|
-
paddingHorizontal: space2.md,
|
|
157
|
-
paddingVertical: space2.sm,
|
|
158
|
-
borderRadius: radius2.md,
|
|
159
|
-
borderWidth: variant === "text" ? 0 : 1,
|
|
160
|
-
borderColor: filled ? colors2.primary : colors2.border,
|
|
161
|
-
backgroundColor: background,
|
|
162
|
-
opacity: inactive ? 0.55 : pressed ? 0.8 : 1,
|
|
163
|
-
alignItems: "center",
|
|
164
|
-
justifyContent: "center",
|
|
165
|
-
flexDirection: "row",
|
|
166
|
-
gap: space2.xs
|
|
167
|
-
}),
|
|
168
|
-
children: [
|
|
169
|
-
props.busy ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: foreground }) : null,
|
|
170
|
-
props.children ?? /* @__PURE__ */ jsxRuntime.jsx(
|
|
171
|
-
reactNative.Text,
|
|
172
|
-
{
|
|
173
|
-
style: {
|
|
174
|
-
color: foreground,
|
|
175
|
-
fontSize: font2.size.md,
|
|
176
|
-
fontWeight: "500",
|
|
177
|
-
...font2.family ? { fontFamily: font2.family } : {}
|
|
178
|
-
},
|
|
179
|
-
children: props.label
|
|
180
|
-
}
|
|
181
|
-
)
|
|
182
|
-
]
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
197
|
function InfoCard(props) {
|
|
187
198
|
const card = cardOf(props.card);
|
|
188
199
|
const { locale, t } = react$1.useCopy();
|
|
@@ -191,7 +202,7 @@ function InfoCard(props) {
|
|
|
191
202
|
const image = safeImage(card.image_url);
|
|
192
203
|
const Img = slots.Image ?? reactNative.Image;
|
|
193
204
|
const money = card.price ?? card.price_from;
|
|
194
|
-
const rating = card.rating ? (card.rating.value_ppm
|
|
205
|
+
const rating = card.rating ? ratingText(card.rating.value_ppm, locale) : null;
|
|
195
206
|
const amount = money ? react$1.formatMoney(money.amount, money.currency, locale) : null;
|
|
196
207
|
const price = amount && !card.price ? t("priceFrom", { price: amount }) : amount;
|
|
197
208
|
const when = card.starts_at ? react$1.formatTimeRange(card.starts_at, void 0, locale, timeZone) : null;
|
|
@@ -234,27 +245,32 @@ function InfoCard(props) {
|
|
|
234
245
|
] })
|
|
235
246
|
] }),
|
|
236
247
|
card.body ? /* @__PURE__ */ jsxRuntime.jsx(Body, { children: card.body }) : null,
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
style: {
|
|
241
|
-
color: colors2.text,
|
|
242
|
-
backgroundColor: colors2.surface,
|
|
243
|
-
borderRadius: radius2.pill,
|
|
244
|
-
paddingHorizontal: space2.sm,
|
|
245
|
-
paddingVertical: space2.xxs,
|
|
246
|
-
fontSize: font2.size.xs
|
|
247
|
-
},
|
|
248
|
-
children: b
|
|
249
|
-
},
|
|
250
|
-
b
|
|
251
|
-
)) }) : null,
|
|
252
|
-
props.restored ? null : /* @__PURE__ */ jsxRuntime.jsx(Actions, { ...props })
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badges, { badges: card.badges }),
|
|
249
|
+
props.restored ? null : /* @__PURE__ */ jsxRuntime.jsx(CardActions, { ...props })
|
|
253
250
|
]
|
|
254
251
|
}
|
|
255
252
|
);
|
|
256
253
|
}
|
|
257
|
-
function
|
|
254
|
+
function Badges(props) {
|
|
255
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
256
|
+
if (!props.badges?.length) return null;
|
|
257
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: space2.xs }, children: props.badges.map((b) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
258
|
+
reactNative.Text,
|
|
259
|
+
{
|
|
260
|
+
style: {
|
|
261
|
+
color: colors2.text,
|
|
262
|
+
backgroundColor: colors2.surface,
|
|
263
|
+
borderRadius: radius2.pill,
|
|
264
|
+
paddingHorizontal: space2.sm,
|
|
265
|
+
paddingVertical: space2.xxs,
|
|
266
|
+
fontSize: font2.size.xs
|
|
267
|
+
},
|
|
268
|
+
children: b
|
|
269
|
+
},
|
|
270
|
+
b
|
|
271
|
+
)) });
|
|
272
|
+
}
|
|
273
|
+
function CardActions(props) {
|
|
258
274
|
const card = cardOf(props.card);
|
|
259
275
|
const { space: space2 } = useTheme();
|
|
260
276
|
if (!card.actions?.length) return null;
|
|
@@ -286,6 +302,120 @@ function Action(props) {
|
|
|
286
302
|
}
|
|
287
303
|
);
|
|
288
304
|
}
|
|
305
|
+
function LinksCard(props) {
|
|
306
|
+
const card = cardOf(props.card);
|
|
307
|
+
const { store, handlers } = useChat();
|
|
308
|
+
const links = (card.links ?? []).filter(
|
|
309
|
+
(l) => /^https:\/\//i.test(l.url) || store.checkLink(l.url)
|
|
310
|
+
);
|
|
311
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
312
|
+
CardFrame,
|
|
313
|
+
{
|
|
314
|
+
label: [card.title, ...links.map((l) => l.label)].join(", "),
|
|
315
|
+
restored: props.restored,
|
|
316
|
+
testID: `vinaverse-card-${card.id}`,
|
|
317
|
+
children: [
|
|
318
|
+
/* @__PURE__ */ jsxRuntime.jsx(Title, { children: card.title }),
|
|
319
|
+
links.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
320
|
+
Button,
|
|
321
|
+
{
|
|
322
|
+
label: l.label,
|
|
323
|
+
role: "link",
|
|
324
|
+
variant: "secondary",
|
|
325
|
+
disabled: props.restored,
|
|
326
|
+
onPress: () => {
|
|
327
|
+
const open = handlers.current?.onOpenLink;
|
|
328
|
+
if (open) open(l.url, props.card);
|
|
329
|
+
else void reactNative.Linking.openURL(l.url);
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
l.url
|
|
333
|
+
))
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
function ListingCard(props) {
|
|
339
|
+
const card = cardOf(props.card);
|
|
340
|
+
const { locale, t } = react$1.useCopy();
|
|
341
|
+
const { slots, timeZone } = useChat();
|
|
342
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
343
|
+
const [failed, setFailed] = react.useState(null);
|
|
344
|
+
const photoUrl = safeImage(card.images?.[0]?.url ?? card.image_url);
|
|
345
|
+
const hero = photoUrl === failed ? null : photoUrl;
|
|
346
|
+
const Img = slots.Image ?? reactNative.Image;
|
|
347
|
+
const rating = card.rating ? `\u2605 ${ratingText(card.rating.value_ppm, locale)} (${card.rating.count})` : null;
|
|
348
|
+
const price = card.price_from ? t("priceFrom", {
|
|
349
|
+
price: react$1.formatMoney(
|
|
350
|
+
card.price_from.amount,
|
|
351
|
+
card.price_from.currency,
|
|
352
|
+
locale
|
|
353
|
+
)
|
|
354
|
+
}) : null;
|
|
355
|
+
const next = card.next_available ? t("nextAvailable", {
|
|
356
|
+
time: react$1.formatTimeRange(card.next_available, void 0, locale, timeZone)
|
|
357
|
+
}) : null;
|
|
358
|
+
const photo = {
|
|
359
|
+
width: "100%",
|
|
360
|
+
aspectRatio: 1.6,
|
|
361
|
+
borderRadius: radius2.md
|
|
362
|
+
};
|
|
363
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
364
|
+
CardFrame,
|
|
365
|
+
{
|
|
366
|
+
label: [card.title, card.subtitle, rating, price, next].filter(Boolean).join(", "),
|
|
367
|
+
restored: props.restored,
|
|
368
|
+
testID: `vinaverse-card-${card.id}`,
|
|
369
|
+
children: [
|
|
370
|
+
hero ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
371
|
+
Img,
|
|
372
|
+
{
|
|
373
|
+
source: { uri: hero },
|
|
374
|
+
onError: () => setFailed(hero),
|
|
375
|
+
style: { ...photo, backgroundColor: colors2.surface },
|
|
376
|
+
accessible: false,
|
|
377
|
+
testID: `vinaverse-photo-${card.id}`
|
|
378
|
+
}
|
|
379
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
380
|
+
reactNative.View,
|
|
381
|
+
{
|
|
382
|
+
accessible: false,
|
|
383
|
+
importantForAccessibility: "no-hide-descendants",
|
|
384
|
+
testID: `vinaverse-monogram-${card.id}`,
|
|
385
|
+
style: {
|
|
386
|
+
...photo,
|
|
387
|
+
backgroundColor: colors2.surface,
|
|
388
|
+
alignItems: "center",
|
|
389
|
+
justifyContent: "center"
|
|
390
|
+
},
|
|
391
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
392
|
+
reactNative.Text,
|
|
393
|
+
{
|
|
394
|
+
style: {
|
|
395
|
+
color: colors2.textMuted,
|
|
396
|
+
fontSize: font2.size.xl,
|
|
397
|
+
fontWeight: "600"
|
|
398
|
+
},
|
|
399
|
+
children: monogram(card.title)
|
|
400
|
+
}
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { gap: space2.xxs }, children: [
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx(Title, { numberOfLines: 2, children: card.title }),
|
|
406
|
+
card.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: card.subtitle }) : null,
|
|
407
|
+
rating || price ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: [rating, price].filter(Boolean).join(" \xB7 ") }) : null
|
|
408
|
+
] }),
|
|
409
|
+
card.body ? /* @__PURE__ */ jsxRuntime.jsx(Body, { children: card.body }) : null,
|
|
410
|
+
/* @__PURE__ */ jsxRuntime.jsx(Badges, { badges: next ? [next, ...card.badges ?? []] : card.badges }),
|
|
411
|
+
props.restored ? null : /* @__PURE__ */ jsxRuntime.jsx(CardActions, { ...props })
|
|
412
|
+
]
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
function monogram(title) {
|
|
417
|
+
return title.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => [...w][0]?.toUpperCase() ?? "").join("");
|
|
418
|
+
}
|
|
289
419
|
function NextStepCard(props) {
|
|
290
420
|
const card = cardOf(props.card);
|
|
291
421
|
const action = card.actions?.[0];
|
|
@@ -1154,7 +1284,10 @@ var DEFAULTS = {
|
|
|
1154
1284
|
proposal: ProposalCard,
|
|
1155
1285
|
question: QuestionCard,
|
|
1156
1286
|
next_step: NextStepCard,
|
|
1157
|
-
time_slots: TimeSlotsCard
|
|
1287
|
+
time_slots: TimeSlotsCard,
|
|
1288
|
+
professional: ListingCard,
|
|
1289
|
+
workspace: ListingCard,
|
|
1290
|
+
links: LinksCard
|
|
1158
1291
|
};
|
|
1159
1292
|
function defaultCard(kind) {
|
|
1160
1293
|
return DEFAULTS[kind] ?? InfoCard;
|
|
@@ -1167,6 +1300,35 @@ var CardView = react.memo(function CardView2(props) {
|
|
|
1167
1300
|
const own = { card, busy, restored: props.item.restored === true };
|
|
1168
1301
|
return Custom ? /* @__PURE__ */ jsxRuntime.jsx(Custom, { ...own, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { ...own });
|
|
1169
1302
|
});
|
|
1303
|
+
var keyOf = (item) => item.key;
|
|
1304
|
+
var kindOf = (item) => item.card.kind;
|
|
1305
|
+
function CardCarousel(props) {
|
|
1306
|
+
const { width } = reactNative.useWindowDimensions();
|
|
1307
|
+
const { t } = react$1.useCopy();
|
|
1308
|
+
const size3 = Math.min(Math.round(width * 0.8), 360);
|
|
1309
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1310
|
+
reactNative.View,
|
|
1311
|
+
{
|
|
1312
|
+
accessibilityRole: "list",
|
|
1313
|
+
accessibilityLabel: t("cardsInRow", {
|
|
1314
|
+
count: String(props.items.length)
|
|
1315
|
+
}),
|
|
1316
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1317
|
+
flashList.FlashList,
|
|
1318
|
+
{
|
|
1319
|
+
horizontal: true,
|
|
1320
|
+
data: props.items,
|
|
1321
|
+
keyExtractor: keyOf,
|
|
1322
|
+
getItemType: kindOf,
|
|
1323
|
+
renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: size3 }, children: /* @__PURE__ */ jsxRuntime.jsx(CardView, { item }) }),
|
|
1324
|
+
snapToInterval: size3,
|
|
1325
|
+
decelerationRate: "fast",
|
|
1326
|
+
showsHorizontalScrollIndicator: false
|
|
1327
|
+
}
|
|
1328
|
+
)
|
|
1329
|
+
}
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1170
1332
|
var ActivityLine = react.memo(function ActivityLine2() {
|
|
1171
1333
|
const { store } = useChat();
|
|
1172
1334
|
const { t } = react$1.useCopy();
|
|
@@ -1473,6 +1635,23 @@ var DisclosureBar = react.memo(function DisclosureBar2() {
|
|
|
1473
1635
|
}
|
|
1474
1636
|
);
|
|
1475
1637
|
});
|
|
1638
|
+
function FailureActionButton(props) {
|
|
1639
|
+
const { handlers, failureAction } = useChat();
|
|
1640
|
+
const { t } = react$1.useCopy();
|
|
1641
|
+
if (!failureAction) return null;
|
|
1642
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1643
|
+
Button,
|
|
1644
|
+
{
|
|
1645
|
+
label: failureAction.label ?? t("talkToPerson"),
|
|
1646
|
+
variant: "text",
|
|
1647
|
+
onPress: () => handlers.current.failureAction?.onPress(props.state)
|
|
1648
|
+
}
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
function FailureSlot(props) {
|
|
1652
|
+
const { slot: Slot, state, Default } = props;
|
|
1653
|
+
return Slot ? /* @__PURE__ */ jsxRuntime.jsx(Slot, { state, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { state });
|
|
1654
|
+
}
|
|
1476
1655
|
function DefaultBubble(props) {
|
|
1477
1656
|
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
1478
1657
|
const role = props.item.role;
|
|
@@ -1628,9 +1807,29 @@ function announce(text) {
|
|
|
1628
1807
|
}
|
|
1629
1808
|
var SystemRow = react.memo(function SystemRow2(props) {
|
|
1630
1809
|
const { t } = react$1.useCopy();
|
|
1631
|
-
const { store } = useChat();
|
|
1632
|
-
const
|
|
1810
|
+
const { store, slots } = useChat();
|
|
1811
|
+
const conversationId = react$1.useConversationSelector(
|
|
1812
|
+
store,
|
|
1813
|
+
(v) => v.conversationId
|
|
1814
|
+
);
|
|
1633
1815
|
const { event, data } = props.item;
|
|
1816
|
+
if (event === "turn_failed") {
|
|
1817
|
+
const key = props.item.key;
|
|
1818
|
+
const state = {
|
|
1819
|
+
kind: "turn_failed",
|
|
1820
|
+
reason: typeof data.code === "string" ? data.code : null,
|
|
1821
|
+
retry: data.retry === true ? () => void store.retryTurn(key) : void 0,
|
|
1822
|
+
conversationId
|
|
1823
|
+
};
|
|
1824
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1825
|
+
FailureSlot,
|
|
1826
|
+
{
|
|
1827
|
+
slot: slots.Error,
|
|
1828
|
+
state,
|
|
1829
|
+
Default: DefaultTurnFailed
|
|
1830
|
+
}
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1634
1833
|
const agent = store.getSnapshot().agent.name ?? t("agentFallbackName");
|
|
1635
1834
|
const text = (() => {
|
|
1636
1835
|
switch (event) {
|
|
@@ -1644,18 +1843,30 @@ var SystemRow = react.memo(function SystemRow2(props) {
|
|
|
1644
1843
|
return data.expected_response ? t("handoffCreated", { expected: data.expected_response }) : t("handoffCreatedNoTime");
|
|
1645
1844
|
case "flow_completed":
|
|
1646
1845
|
return data.result === "ok" ? t("flowSent") : data.result === "failed" ? t("flowNotSent") : null;
|
|
1647
|
-
case "turn_failed":
|
|
1648
|
-
return t("turnFailed");
|
|
1649
1846
|
default:
|
|
1650
1847
|
return null;
|
|
1651
1848
|
}
|
|
1652
1849
|
})();
|
|
1653
1850
|
if (!text) return null;
|
|
1851
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Line, { text });
|
|
1852
|
+
});
|
|
1853
|
+
function DefaultTurnFailed(props) {
|
|
1854
|
+
const { t } = react$1.useCopy();
|
|
1855
|
+
const { failureAction } = useChat();
|
|
1856
|
+
const text = t("turnFailed");
|
|
1857
|
+
if (!failureAction) return /* @__PURE__ */ jsxRuntime.jsx(Line, { text, danger: true });
|
|
1858
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { alignItems: "center" }, children: [
|
|
1859
|
+
/* @__PURE__ */ jsxRuntime.jsx(Line, { text, danger: true }),
|
|
1860
|
+
/* @__PURE__ */ jsxRuntime.jsx(FailureActionButton, { state: props.state })
|
|
1861
|
+
] });
|
|
1862
|
+
}
|
|
1863
|
+
function Line(props) {
|
|
1864
|
+
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1654
1865
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1655
1866
|
reactNative.View,
|
|
1656
1867
|
{
|
|
1657
1868
|
accessible: true,
|
|
1658
|
-
accessibilityLabel: text,
|
|
1869
|
+
accessibilityLabel: props.text,
|
|
1659
1870
|
style: {
|
|
1660
1871
|
alignItems: "center",
|
|
1661
1872
|
paddingHorizontal: space2.xl,
|
|
@@ -1665,17 +1876,54 @@ var SystemRow = react.memo(function SystemRow2(props) {
|
|
|
1665
1876
|
reactNative.Text,
|
|
1666
1877
|
{
|
|
1667
1878
|
style: {
|
|
1668
|
-
color:
|
|
1879
|
+
color: props.danger ? colors2.danger : colors2.textMuted,
|
|
1669
1880
|
fontSize: font2.size.sm,
|
|
1670
1881
|
textAlign: "center"
|
|
1671
1882
|
},
|
|
1672
|
-
children: text
|
|
1883
|
+
children: props.text
|
|
1673
1884
|
}
|
|
1674
1885
|
)
|
|
1675
1886
|
}
|
|
1676
1887
|
);
|
|
1888
|
+
}
|
|
1889
|
+
var Welcome = react.memo(function Welcome2(props) {
|
|
1890
|
+
const { colors: colors2, radius: radius2, space: space2, font: font2 } = useTheme();
|
|
1891
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1892
|
+
reactNative.View,
|
|
1893
|
+
{
|
|
1894
|
+
style: { padding: space2.lg, gap: space2.md },
|
|
1895
|
+
testID: "vinaverse-welcome",
|
|
1896
|
+
children: [
|
|
1897
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1898
|
+
reactNative.Text,
|
|
1899
|
+
{
|
|
1900
|
+
style: {
|
|
1901
|
+
alignSelf: "flex-start",
|
|
1902
|
+
maxWidth: "85%",
|
|
1903
|
+
color: colors2.agentText,
|
|
1904
|
+
backgroundColor: colors2.agentBubble,
|
|
1905
|
+
borderRadius: radius2.lg,
|
|
1906
|
+
paddingHorizontal: space2.md,
|
|
1907
|
+
paddingVertical: space2.sm,
|
|
1908
|
+
fontSize: font2.size.md,
|
|
1909
|
+
lineHeight: font2.size.md * font2.lineHeight
|
|
1910
|
+
},
|
|
1911
|
+
children: props.welcome.text
|
|
1912
|
+
}
|
|
1913
|
+
),
|
|
1914
|
+
props.welcome.chips.length ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { flexDirection: "row", flexWrap: "wrap", gap: space2.sm }, children: props.welcome.chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1915
|
+
Button,
|
|
1916
|
+
{
|
|
1917
|
+
label: chip.label,
|
|
1918
|
+
onPress: () => props.onChip(chip.message)
|
|
1919
|
+
},
|
|
1920
|
+
chip.label
|
|
1921
|
+
)) }) : null
|
|
1922
|
+
]
|
|
1923
|
+
}
|
|
1924
|
+
);
|
|
1677
1925
|
});
|
|
1678
|
-
var
|
|
1926
|
+
var keyOf2 = (item) => item.key;
|
|
1679
1927
|
function itemType(item) {
|
|
1680
1928
|
if (item.type === "card") return `card:${item.card.kind}`;
|
|
1681
1929
|
if (item.type === "message")
|
|
@@ -1685,8 +1933,39 @@ function itemType(item) {
|
|
|
1685
1933
|
function authorOf(item) {
|
|
1686
1934
|
return item?.type === "message" ? item.role : null;
|
|
1687
1935
|
}
|
|
1936
|
+
function carousels(items, previous) {
|
|
1937
|
+
const runs = /* @__PURE__ */ new Map();
|
|
1938
|
+
const inside = /* @__PURE__ */ new Set();
|
|
1939
|
+
const groupOf = (item) => {
|
|
1940
|
+
if (item?.type !== "card") return null;
|
|
1941
|
+
const group = item.card.card.group;
|
|
1942
|
+
return group?.layout === "carousel" && typeof group.id === "string" ? group.id : null;
|
|
1943
|
+
};
|
|
1944
|
+
for (let i = 0; i < items.length; i++) {
|
|
1945
|
+
const id = groupOf(items[i]);
|
|
1946
|
+
if (id === null) continue;
|
|
1947
|
+
let end = i + 1;
|
|
1948
|
+
while (end < items.length && groupOf(items[end]) === id) end++;
|
|
1949
|
+
if (end - i > 1) {
|
|
1950
|
+
const run = items.slice(i, end);
|
|
1951
|
+
const key = run[0]?.key;
|
|
1952
|
+
const before = previous.get(key);
|
|
1953
|
+
runs.set(
|
|
1954
|
+
key,
|
|
1955
|
+
before && before.length === run.length && before.every((c, n) => c === run[n]) ? before : run
|
|
1956
|
+
);
|
|
1957
|
+
for (const card of run.slice(1)) inside.add(card.key);
|
|
1958
|
+
}
|
|
1959
|
+
i = end - 1;
|
|
1960
|
+
}
|
|
1961
|
+
return { runs, inside };
|
|
1962
|
+
}
|
|
1688
1963
|
var Row = react.memo(function Row2(props) {
|
|
1689
1964
|
const { item } = props;
|
|
1965
|
+
const { slots } = useChat();
|
|
1966
|
+
const Group = slots.CardGroup;
|
|
1967
|
+
if (props.inside) return null;
|
|
1968
|
+
if (props.run && Group) return /* @__PURE__ */ jsxRuntime.jsx(Group, { items: props.run });
|
|
1690
1969
|
if (item.type === "card") return /* @__PURE__ */ jsxRuntime.jsx(CardView, { item });
|
|
1691
1970
|
if (item.type === "system") return /* @__PURE__ */ jsxRuntime.jsx(SystemRow, { item });
|
|
1692
1971
|
return item.state === "streaming" ? /* @__PURE__ */ jsxRuntime.jsx(StreamingBubble, { item, showAuthor: props.showAuthor }) : /* @__PURE__ */ jsxRuntime.jsx(MessageBubble, { item, showAuthor: props.showAuthor });
|
|
@@ -1702,26 +1981,49 @@ var MessageList = react.memo(function MessageList2() {
|
|
|
1702
1981
|
(v) => v.status === "starting" || v.status === "connecting"
|
|
1703
1982
|
);
|
|
1704
1983
|
const agentName = react$1.useConversationSelector(store, (v) => v.agent.name);
|
|
1984
|
+
const welcome = react$1.useConversationSelector(store, (v) => v.welcome);
|
|
1705
1985
|
useAnnounceReplies(items);
|
|
1986
|
+
const Group = slots.CardGroup;
|
|
1987
|
+
const runsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
1988
|
+
const grouping = react.useMemo(
|
|
1989
|
+
() => Group ? carousels(items, runsRef.current) : null,
|
|
1990
|
+
[items, Group]
|
|
1991
|
+
);
|
|
1992
|
+
runsRef.current = grouping?.runs ?? /* @__PURE__ */ new Map();
|
|
1706
1993
|
const itemsRef = react.useRef(items);
|
|
1707
1994
|
itemsRef.current = items;
|
|
1995
|
+
const groupingRef = react.useRef(grouping);
|
|
1996
|
+
groupingRef.current = grouping;
|
|
1708
1997
|
const renderItem = react.useCallback(
|
|
1709
1998
|
({ item, index }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1710
1999
|
Row,
|
|
1711
2000
|
{
|
|
1712
2001
|
item,
|
|
1713
|
-
showAuthor: authorOf(item) !== authorOf(itemsRef.current[index - 1])
|
|
2002
|
+
showAuthor: authorOf(item) !== authorOf(itemsRef.current[index - 1]),
|
|
2003
|
+
run: groupingRef.current?.runs.get(item.key),
|
|
2004
|
+
inside: groupingRef.current?.inside.has(item.key) === true
|
|
1714
2005
|
}
|
|
1715
2006
|
),
|
|
1716
2007
|
[]
|
|
1717
2008
|
);
|
|
2009
|
+
const onChip = react.useCallback(
|
|
2010
|
+
(message) => void store.send(message),
|
|
2011
|
+
[store]
|
|
2012
|
+
);
|
|
1718
2013
|
const Empty = slots.EmptyState;
|
|
1719
|
-
const empty = opening ? /* @__PURE__ */ jsxRuntime.jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2014
|
+
const empty = opening ? /* @__PURE__ */ jsxRuntime.jsx(ChatSkeleton, {}) : Empty ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2015
|
+
Empty,
|
|
2016
|
+
{
|
|
2017
|
+
agentName: agentName ?? t("agentFallbackName"),
|
|
2018
|
+
welcome,
|
|
2019
|
+
onChip
|
|
2020
|
+
}
|
|
2021
|
+
) : welcome ? /* @__PURE__ */ jsxRuntime.jsx(Welcome, { welcome, onChip }) : null;
|
|
1720
2022
|
const props = react.useMemo(
|
|
1721
2023
|
() => ({
|
|
1722
2024
|
data: items,
|
|
1723
2025
|
renderItem,
|
|
1724
|
-
keyExtractor:
|
|
2026
|
+
keyExtractor: keyOf2,
|
|
1725
2027
|
getItemType: itemType,
|
|
1726
2028
|
maintainVisibleContentPosition: {
|
|
1727
2029
|
startRenderingFromBottom: true,
|
|
@@ -1759,10 +2061,23 @@ function useAnnounceReplies(items) {
|
|
|
1759
2061
|
}
|
|
1760
2062
|
var CONNECTING_DELAY = tokens.motion.bannerDelay;
|
|
1761
2063
|
var StatusBanner = react.memo(function StatusBanner2() {
|
|
1762
|
-
const { store } = useChat();
|
|
2064
|
+
const { store, slots } = useChat();
|
|
1763
2065
|
const { t } = react$1.useCopy();
|
|
1764
|
-
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1765
2066
|
const banner = react$1.useConversationSelector(store, (v) => v.banner);
|
|
2067
|
+
const status = react$1.useConversationSelector(store, (v) => v.status);
|
|
2068
|
+
const conversationId = react$1.useConversationSelector(
|
|
2069
|
+
store,
|
|
2070
|
+
(v) => v.conversationId
|
|
2071
|
+
);
|
|
2072
|
+
const unavailable = react.useMemo(
|
|
2073
|
+
() => ({
|
|
2074
|
+
kind: "unavailable",
|
|
2075
|
+
reason: status,
|
|
2076
|
+
retry: () => store.start(),
|
|
2077
|
+
conversationId
|
|
2078
|
+
}),
|
|
2079
|
+
[store, status, conversationId]
|
|
2080
|
+
);
|
|
1766
2081
|
const [late, setLate] = react.useState(false);
|
|
1767
2082
|
react.useEffect(() => {
|
|
1768
2083
|
setLate(false);
|
|
@@ -1771,12 +2086,20 @@ var StatusBanner = react.memo(function StatusBanner2() {
|
|
|
1771
2086
|
return () => clearTimeout(timer);
|
|
1772
2087
|
}, [banner]);
|
|
1773
2088
|
if (banner === null || banner === "connecting" && !late) return null;
|
|
2089
|
+
if (banner === "unavailable")
|
|
2090
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2091
|
+
FailureSlot,
|
|
2092
|
+
{
|
|
2093
|
+
slot: slots.Unavailable,
|
|
2094
|
+
state: unavailable,
|
|
2095
|
+
Default: DefaultUnavailable
|
|
2096
|
+
}
|
|
2097
|
+
);
|
|
1774
2098
|
const text = {
|
|
1775
2099
|
connecting: t("bannerConnecting"),
|
|
1776
2100
|
reconnecting: t("bannerReconnecting"),
|
|
1777
2101
|
offline: t("bannerOffline"),
|
|
1778
|
-
continued_elsewhere: t("bannerContinuedElsewhere")
|
|
1779
|
-
unavailable: t("bannerUnavailable")
|
|
2102
|
+
continued_elsewhere: t("bannerContinuedElsewhere")
|
|
1780
2103
|
}[banner];
|
|
1781
2104
|
const action = banner === "continued_elsewhere" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1782
2105
|
Button,
|
|
@@ -1785,7 +2108,7 @@ var StatusBanner = react.memo(function StatusBanner2() {
|
|
|
1785
2108
|
variant: "text",
|
|
1786
2109
|
onPress: () => void store.continueHere()
|
|
1787
2110
|
}
|
|
1788
|
-
) : banner === "offline"
|
|
2111
|
+
) : banner === "offline" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1789
2112
|
Button,
|
|
1790
2113
|
{
|
|
1791
2114
|
label: t("tryAgain"),
|
|
@@ -1793,6 +2116,18 @@ var StatusBanner = react.memo(function StatusBanner2() {
|
|
|
1793
2116
|
onPress: () => store.start()
|
|
1794
2117
|
}
|
|
1795
2118
|
) : null;
|
|
2119
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Banner, { text, children: action });
|
|
2120
|
+
});
|
|
2121
|
+
function DefaultUnavailable(props) {
|
|
2122
|
+
const { t } = react$1.useCopy();
|
|
2123
|
+
const { retry } = props.state;
|
|
2124
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Banner, { text: t("bannerUnavailable"), children: [
|
|
2125
|
+
retry ? /* @__PURE__ */ jsxRuntime.jsx(Button, { label: t("tryAgain"), variant: "text", onPress: retry }) : null,
|
|
2126
|
+
/* @__PURE__ */ jsxRuntime.jsx(FailureActionButton, { state: props.state })
|
|
2127
|
+
] });
|
|
2128
|
+
}
|
|
2129
|
+
function Banner(props) {
|
|
2130
|
+
const { colors: colors2, space: space2, font: font2 } = useTheme();
|
|
1796
2131
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1797
2132
|
reactNative.View,
|
|
1798
2133
|
{
|
|
@@ -1814,22 +2149,35 @@ var StatusBanner = react.memo(function StatusBanner2() {
|
|
|
1814
2149
|
reactNative.Text,
|
|
1815
2150
|
{
|
|
1816
2151
|
style: { color: colors2.text, fontSize: font2.size.sm, flexShrink: 1 },
|
|
1817
|
-
children: text
|
|
2152
|
+
children: props.text
|
|
1818
2153
|
}
|
|
1819
2154
|
),
|
|
1820
|
-
|
|
2155
|
+
props.children
|
|
1821
2156
|
]
|
|
1822
2157
|
}
|
|
1823
2158
|
);
|
|
1824
|
-
}
|
|
2159
|
+
}
|
|
1825
2160
|
function VinaverseChatProvider(props) {
|
|
1826
2161
|
const { conversation, components, agentAvatar, timeZone } = props;
|
|
1827
2162
|
const handlers = react.useRef(props);
|
|
1828
2163
|
handlers.current = props;
|
|
1829
2164
|
const slots = useStableSlots(components);
|
|
2165
|
+
const hasAction = props.failureAction !== void 0;
|
|
2166
|
+
const actionLabel = props.failureAction?.label;
|
|
2167
|
+
const failureAction = react.useMemo(
|
|
2168
|
+
() => hasAction ? { label: actionLabel } : null,
|
|
2169
|
+
[hasAction, actionLabel]
|
|
2170
|
+
);
|
|
1830
2171
|
const value = react.useMemo(
|
|
1831
|
-
() => ({
|
|
1832
|
-
|
|
2172
|
+
() => ({
|
|
2173
|
+
store: conversation,
|
|
2174
|
+
handlers,
|
|
2175
|
+
slots,
|
|
2176
|
+
agentAvatar,
|
|
2177
|
+
timeZone,
|
|
2178
|
+
failureAction
|
|
2179
|
+
}),
|
|
2180
|
+
[conversation, slots, agentAvatar, timeZone, failureAction]
|
|
1833
2181
|
);
|
|
1834
2182
|
useNativeLifecycle(conversation);
|
|
1835
2183
|
react.useEffect(() => {
|
|
@@ -1908,6 +2256,8 @@ function sameSlots(a, b) {
|
|
|
1908
2256
|
exports.ActivityLine = ActivityLine;
|
|
1909
2257
|
exports.Avatar = Avatar;
|
|
1910
2258
|
exports.Button = Button;
|
|
2259
|
+
exports.CardActions = CardActions;
|
|
2260
|
+
exports.CardCarousel = CardCarousel;
|
|
1911
2261
|
exports.CardFrame = CardFrame;
|
|
1912
2262
|
exports.CardView = CardView;
|
|
1913
2263
|
exports.ChatHeader = ChatHeader;
|
|
@@ -1916,8 +2266,13 @@ exports.Composer = Composer;
|
|
|
1916
2266
|
exports.DefaultBubble = DefaultBubble;
|
|
1917
2267
|
exports.DefaultComposer = DefaultComposer;
|
|
1918
2268
|
exports.DefaultHeader = DefaultHeader;
|
|
2269
|
+
exports.DefaultTurnFailed = DefaultTurnFailed;
|
|
2270
|
+
exports.DefaultUnavailable = DefaultUnavailable;
|
|
1919
2271
|
exports.DisclosureBar = DisclosureBar;
|
|
2272
|
+
exports.FailureActionButton = FailureActionButton;
|
|
1920
2273
|
exports.InfoCard = InfoCard;
|
|
2274
|
+
exports.LinksCard = LinksCard;
|
|
2275
|
+
exports.ListingCard = ListingCard;
|
|
1921
2276
|
exports.MessageBubble = MessageBubble;
|
|
1922
2277
|
exports.MessageList = MessageList;
|
|
1923
2278
|
exports.NextStepCard = NextStepCard;
|
|
@@ -1931,12 +2286,16 @@ exports.ThemeProvider = ThemeProvider;
|
|
|
1931
2286
|
exports.TimeSlotsCard = TimeSlotsCard;
|
|
1932
2287
|
exports.VinaverseChat = VinaverseChat;
|
|
1933
2288
|
exports.VinaverseChatProvider = VinaverseChatProvider;
|
|
2289
|
+
exports.Welcome = Welcome;
|
|
1934
2290
|
exports.announce = announce;
|
|
1935
2291
|
exports.answerText = answerText;
|
|
2292
|
+
exports.carousels = carousels;
|
|
1936
2293
|
exports.createTheme = createTheme;
|
|
1937
2294
|
exports.defaultCard = defaultCard;
|
|
1938
2295
|
exports.fieldText = fieldText;
|
|
1939
2296
|
exports.itemType = itemType;
|
|
2297
|
+
exports.monogram = monogram;
|
|
2298
|
+
exports.ratingText = ratingText;
|
|
1940
2299
|
exports.themeProblems = themeProblems;
|
|
1941
2300
|
exports.useChat = useChat;
|
|
1942
2301
|
exports.useNativeLifecycle = useNativeLifecycle;
|