@vinaverse/react-native 0.1.0-alpha.7 → 0.1.0-alpha.9

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.
@@ -171,7 +171,7 @@ function safeImage(url) {
171
171
  function InfoCard(props) {
172
172
  const card = cardOf(props.card);
173
173
  const { locale, t } = react$1.useCopy();
174
- const { slots, timeZone } = useChat();
174
+ const { slots, timeZone, handlers } = useChat();
175
175
  const { colors, radius, space, font } = chunk6QYDPGRS_cjs.useTheme();
176
176
  const image = safeImage(card.image_url);
177
177
  const Img = slots.Image ?? reactNative.Image;
@@ -180,13 +180,42 @@ function InfoCard(props) {
180
180
  const amount = money ? react$1.formatMoney(money.amount, money.currency, locale) : null;
181
181
  const price = amount && !card.price ? t("priceFrom", { price: amount }) : amount;
182
182
  const when = card.starts_at ? react$1.formatTimeRange(card.starts_at, void 0, locale, timeZone) : null;
183
+ const status = typeof card.status === "string" && card.status ? card.status : null;
184
+ const ref = card.ref;
185
+ const onOpenTarget = handlers.current?.onOpenTarget;
186
+ const open = ref && onOpenTarget && !props.restored ? () => onOpenTarget({ type: ref.type, id: ref.id }, props.card) : null;
183
187
  const label = [
184
188
  card.title,
185
189
  card.subtitle,
190
+ status,
186
191
  rating && `\u2605 ${rating}`,
187
192
  price,
188
193
  when
189
194
  ].filter(Boolean).join(", ");
195
+ const top = /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", gap: space.md }, children: [
196
+ image ? /* @__PURE__ */ jsxRuntime.jsx(
197
+ Img,
198
+ {
199
+ source: { uri: image },
200
+ style: {
201
+ width: 64,
202
+ height: 64,
203
+ borderRadius: radius.md,
204
+ backgroundColor: colors.surface
205
+ },
206
+ accessible: false
207
+ }
208
+ ) : null,
209
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: space.xxs }, children: [
210
+ /* @__PURE__ */ jsxRuntime.jsx(Title, { numberOfLines: 2, children: card.title }),
211
+ card.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: card.subtitle }) : null,
212
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors.textMuted, fontSize: font.size.sm }, children: [
213
+ rating && `\u2605 ${rating}${card.rating ? ` (${card.rating.count})` : ""}`,
214
+ price,
215
+ when
216
+ ].filter(Boolean).join(" \xB7 ") })
217
+ ] })
218
+ ] });
190
219
  return /* @__PURE__ */ jsxRuntime.jsxs(
191
220
  CardFrame,
192
221
  {
@@ -194,32 +223,23 @@ function InfoCard(props) {
194
223
  restored: props.restored,
195
224
  testID: `vinaverse-card-${card.id}`,
196
225
  children: [
197
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flexDirection: "row", gap: space.md }, children: [
198
- image ? /* @__PURE__ */ jsxRuntime.jsx(
199
- Img,
200
- {
201
- source: { uri: image },
202
- style: {
203
- width: 64,
204
- height: 64,
205
- borderRadius: radius.md,
206
- backgroundColor: colors.surface
207
- },
208
- accessible: false
209
- }
210
- ) : null,
211
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: { flex: 1, gap: space.xxs }, children: [
212
- /* @__PURE__ */ jsxRuntime.jsx(Title, { numberOfLines: 2, children: card.title }),
213
- card.subtitle ? /* @__PURE__ */ jsxRuntime.jsx(Body, { muted: true, children: card.subtitle }) : null,
214
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: { color: colors.textMuted, fontSize: font.size.sm }, children: [
215
- rating && `\u2605 ${rating}${card.rating ? ` (${card.rating.count})` : ""}`,
216
- price,
217
- when
218
- ].filter(Boolean).join(" \xB7 ") })
219
- ] })
220
- ] }),
226
+ open ? /* @__PURE__ */ jsxRuntime.jsx(
227
+ reactNative.Pressable,
228
+ {
229
+ accessibilityRole: "link",
230
+ accessibilityLabel: card.title,
231
+ onPress: open,
232
+ testID: `vinaverse-open-${card.id}`,
233
+ children: top
234
+ }
235
+ ) : top,
221
236
  card.body ? /* @__PURE__ */ jsxRuntime.jsx(Body, { children: card.body }) : null,
222
- /* @__PURE__ */ jsxRuntime.jsx(Badges, { badges: card.badges }),
237
+ /* @__PURE__ */ jsxRuntime.jsx(
238
+ Badges,
239
+ {
240
+ badges: status ? [status, ...card.badges ?? []] : card.badges
241
+ }
242
+ ),
223
243
  props.restored ? null : /* @__PURE__ */ jsxRuntime.jsx(CardActions, { ...props })
224
244
  ]
225
245
  }
@@ -321,7 +341,7 @@ function ListingCard(props) {
321
341
  );
322
342
  const hero = photoUrl === failed ? null : photoUrl;
323
343
  const Img = slots.Image ?? reactNative.Image;
324
- const rating = card.rating ? `\u2605 ${ratingText(card.rating.value_ppm, locale)} (${card.rating.count})` : null;
344
+ const rating = card.rating ? `\u2605 ${ratingText(card.rating.value_ppm, locale)} (${card.rating.count})` : t("ratingNone") || null;
325
345
  const price = card.price_from ? t("priceFrom", {
326
346
  price: react$1.formatMoney(
327
347
  card.price_from.amount,
@@ -330,14 +350,24 @@ function ListingCard(props) {
330
350
  )
331
351
  }) : null;
332
352
  const next = card.next_available ? t("nextAvailable", {
333
- time: react$1.formatTimeRange(card.next_available, void 0, locale, timeZone)
353
+ // "today, 10:00 AM", "tomorrow, 10:00 AM", else the date (the copy's `today` and `tomorrow`).
354
+ time: react$1.formatDayTime(
355
+ card.next_available,
356
+ { today: t("today"), tomorrow: t("tomorrow") },
357
+ locale,
358
+ timeZone
359
+ )
334
360
  }) : null;
335
361
  const ref = card.ref;
336
362
  const onOpenTarget = handlers.current?.onOpenTarget;
337
363
  const open = ref && onOpenTarget && !props.restored ? () => onOpenTarget({ type: ref.type, id: ref.id }, props.card) : null;
364
+ const asked = imagePolicy?.aspectRatio;
365
+ const size2 = card.images?.[0];
366
+ const range = imagePolicy?.naturalAspect ?? { min: 1, max: 2 };
367
+ const own = asked === "natural" && !props.grouped && size2?.width && size2.height ? Math.min(range.max, Math.max(range.min, size2.width / size2.height)) : null;
338
368
  const photo = {
339
369
  width: "100%",
340
- aspectRatio: imagePolicy?.aspectRatio ?? 1.6,
370
+ aspectRatio: own ?? (typeof asked === "number" ? asked : 1.6),
341
371
  borderRadius: radius.md
342
372
  };
343
373
  const top = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -350,7 +380,7 @@ function ListingCard(props) {
350
380
  accessible: false,
351
381
  testID: `vinaverse-photo-${card.id}`
352
382
  }
353
- ) : imagePolicy?.Placeholder ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: photo, testID: `vinaverse-placeholder-${card.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(imagePolicy.Placeholder, { title: card.title }) }) : /* @__PURE__ */ jsxRuntime.jsx(
383
+ ) : imagePolicy?.Placeholder ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: photo, testID: `vinaverse-placeholder-${card.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(imagePolicy.Placeholder, { title: card.title, kind: card.kind }) }) : /* @__PURE__ */ jsxRuntime.jsx(
354
384
  reactNative.View,
355
385
  {
356
386
  accessible: false,
@@ -505,7 +535,9 @@ function ProposalCard(props) {
505
535
  props.card.id,
506
536
  () => onProposal(props.card, decision)
507
537
  ).catch(
508
- (error) => handlers.current?.onWarning?.(`Vinaverse proposal: ${String(error)}`)
538
+ (error) => handlers.current?.onWarning?.(`Vinaverse proposal: ${String(error)}`, {
539
+ code: "proposal_failed"
540
+ })
509
541
  );
510
542
  };
511
543
  const fields = proposal.fields.map((f) => {
@@ -627,7 +659,9 @@ function QuestionCard(props) {
627
659
  try {
628
660
  store.answer(held.id, questionId, answer);
629
661
  } catch (error) {
630
- handlers.current?.onWarning?.(`Vinaverse answer: ${String(error)}`);
662
+ handlers.current?.onWarning?.(`Vinaverse answer: ${String(error)}`, {
663
+ code: "answer_failed"
664
+ });
631
665
  }
632
666
  };
633
667
  const locked = held.locked.includes(question.question_id);
@@ -738,7 +772,8 @@ function Answer(props) {
738
772
  if (picked) send({ upload_ref: picked.upload_ref });
739
773
  }).catch(
740
774
  (error) => handlers.current?.onWarning?.(
741
- `Vinaverse photo: ${String(error)}`
775
+ `Vinaverse photo: ${String(error)}`,
776
+ { code: "photo_failed" }
742
777
  )
743
778
  );
744
779
  }
@@ -1291,7 +1326,13 @@ var CardView = /* @__PURE__ */ react.memo(function CardView2(props) {
1291
1326
  const Default = defaultCard(card.kind);
1292
1327
  const Custom = slots.cards?.[card.kind];
1293
1328
  const read = react.useMemo(() => agent$1.readCard(card.card), [card]);
1294
- const own = { card, read, busy, restored: props.item.restored === true };
1329
+ const own = {
1330
+ card,
1331
+ read,
1332
+ busy,
1333
+ restored: props.item.restored === true,
1334
+ grouped: props.grouped === true
1335
+ };
1295
1336
  return Custom ? /* @__PURE__ */ jsxRuntime.jsx(Custom, { ...own, Default }) : /* @__PURE__ */ jsxRuntime.jsx(Default, { ...own });
1296
1337
  });
1297
1338
  var keyOf = (item) => item.key;
@@ -1314,7 +1355,7 @@ function CardCarousel(props) {
1314
1355
  data: props.items,
1315
1356
  keyExtractor: keyOf,
1316
1357
  getItemType: kindOf,
1317
- renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: size2 }, children: /* @__PURE__ */ jsxRuntime.jsx(CardView, { item }) }),
1358
+ renderItem: ({ item }) => /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: { width: size2 }, children: /* @__PURE__ */ jsxRuntime.jsx(CardView, { item, grouped: true }) }),
1318
1359
  snapToInterval: size2,
1319
1360
  decelerationRate: "fast",
1320
1361
  showsHorizontalScrollIndicator: false
@@ -1900,29 +1941,27 @@ function authorOf(item) {
1900
1941
  return item?.type === "message" ? item.role : null;
1901
1942
  }
1902
1943
  function carousels(items, previous) {
1944
+ const groups = /* @__PURE__ */ new Map();
1945
+ for (const item of items) {
1946
+ if (item.type !== "card") continue;
1947
+ const group = item.card.card.group;
1948
+ if (group?.layout !== "carousel" || typeof group.id !== "string") continue;
1949
+ const id = `${item.card.turnId ?? ""}\0${group.id}`;
1950
+ const members = groups.get(id);
1951
+ if (members) members.push(item);
1952
+ else groups.set(id, [item]);
1953
+ }
1903
1954
  const runs = /* @__PURE__ */ new Map();
1904
1955
  const inside = /* @__PURE__ */ new Set();
1905
- const groupOf = (item) => {
1906
- if (item?.type !== "card") return null;
1907
- const group = item.card.card.group;
1908
- return group?.layout === "carousel" && typeof group.id === "string" ? group.id : null;
1909
- };
1910
- for (let i = 0; i < items.length; i++) {
1911
- const id = groupOf(items[i]);
1912
- if (id === null) continue;
1913
- let end = i + 1;
1914
- while (end < items.length && groupOf(items[end]) === id) end++;
1915
- if (end - i > 1) {
1916
- const run = items.slice(i, end);
1917
- const key = run[0]?.key;
1918
- const before = previous.get(key);
1919
- runs.set(
1920
- key,
1921
- before && before.length === run.length && before.every((c, n) => c === run[n]) ? before : run
1922
- );
1923
- for (const card of run.slice(1)) inside.add(card.key);
1924
- }
1925
- i = end - 1;
1956
+ for (const run of groups.values()) {
1957
+ if (run.length < 2) continue;
1958
+ const key = run[0]?.key;
1959
+ const before = previous.get(key);
1960
+ runs.set(
1961
+ key,
1962
+ before && before.length === run.length && before.every((c, n) => c === run[n]) ? before : run
1963
+ );
1964
+ for (const card of run.slice(1)) inside.add(card.key);
1926
1965
  }
1927
1966
  return { runs, inside };
1928
1967
  }
@@ -2199,10 +2238,11 @@ function VinaverseChatProvider(props) {
2199
2238
  react.useEffect(() => {
2200
2239
  if (!onWarning) return;
2201
2240
  for (const problem of chunk6QYDPGRS_cjs.themeProblems(theme))
2202
- onWarning(`Vinaverse theme: ${problem}`);
2241
+ onWarning(`Vinaverse theme: ${problem}`, { code: "theme" });
2203
2242
  for (const key of react$1.bannedWordsIn(copy ?? {}))
2204
2243
  onWarning(
2205
- `Vinaverse copy: "${key}" says assistant, bot or crew; the copy says agent.`
2244
+ `Vinaverse copy: "${key}" says assistant, bot or crew; the copy says agent.`,
2245
+ { code: "copy" }
2206
2246
  );
2207
2247
  }, [onWarning, theme, copy]);
2208
2248
  return /* @__PURE__ */ jsxRuntime.jsx(react$1.CopyProvider, { locale: props.locale, copy: props.copy, children: /* @__PURE__ */ jsxRuntime.jsx(chunk6QYDPGRS_cjs.ThemeProvider, { theme: props.theme, colorScheme: props.colorScheme, children: /* @__PURE__ */ jsxRuntime.jsx(ChatContextProvider, { value, children: props.children }) }) });
@@ -2226,7 +2266,7 @@ function Layout(props) {
2226
2266
  const { direction } = react$1.useCopy();
2227
2267
  const { onError } = props;
2228
2268
  const reportError = react.useMemo(
2229
- () => onError ? (e) => onError(String(e)) : void 0,
2269
+ () => onError ? (e) => onError(String(e), { code: "send_failed" }) : void 0,
2230
2270
  [onError]
2231
2271
  );
2232
2272
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2598,6 +2638,10 @@ Object.defineProperty(exports, "useAgentStatus", {
2598
2638
  enumerable: true,
2599
2639
  get: function () { return agent.useAgentStatus; }
2600
2640
  });
2641
+ Object.defineProperty(exports, "useAgentStatusState", {
2642
+ enumerable: true,
2643
+ get: function () { return agent.useAgentStatusState; }
2644
+ });
2601
2645
  exports.ActivityLine = ActivityLine;
2602
2646
  exports.AgentChat = AgentChat;
2603
2647
  exports.AgentFooter = AgentFooter;
@@ -2646,5 +2690,5 @@ exports.monogram = monogram;
2646
2690
  exports.newestWholeReply = newestWholeReply;
2647
2691
  exports.ratingText = ratingText;
2648
2692
  exports.useChat = useChat;
2649
- //# sourceMappingURL=chunk-AGZ7IH7E.cjs.map
2650
- //# sourceMappingURL=chunk-AGZ7IH7E.cjs.map
2693
+ //# sourceMappingURL=chunk-V7XYOLLT.cjs.map
2694
+ //# sourceMappingURL=chunk-V7XYOLLT.cjs.map