@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/README.md
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A ready, themeable conversation with your Vinaverse agent for React Native and Expo apps. Drop it into a screen,
|
|
4
4
|
give it a conversation whose session comes from your backend, and it draws everything the realtime protocol defines:
|
|
5
|
-
streaming replies, cards (people, services, bookings, questions, proposals, next steps
|
|
5
|
+
streaming replies, cards (photo cards for people and places, services, bookings, questions, proposals, next steps,
|
|
6
|
+
your own help pages), held and unsent messages, a
|
|
6
7
|
person from your business taking over, and every way a session ends. No native module; New Architecture; FlashList 2.3.2 or later.
|
|
7
8
|
|
|
8
9
|
```tsx
|
|
9
10
|
import { createConversation } from '@vinaverse/client/conversation';
|
|
10
|
-
import { VinaverseChat } from '@vinaverse/react-native';
|
|
11
|
+
import { CardCarousel, VinaverseChat } from '@vinaverse/react-native';
|
|
11
12
|
|
|
12
13
|
// Outside the screen: it connects early and survives navigation.
|
|
13
14
|
const conversation = createConversation({
|
|
@@ -15,6 +16,8 @@ const conversation = createConversation({
|
|
|
15
16
|
locale: 'es-US',
|
|
16
17
|
deepLinkPrefixes: ['yourapp://bookings/'],
|
|
17
18
|
});
|
|
19
|
+
// Opt-in: a turn's listing cards in one swipeable row (without it they stack). Module level: a stable object.
|
|
20
|
+
const components = { CardGroup: CardCarousel };
|
|
18
21
|
|
|
19
22
|
export function AgentScreen() {
|
|
20
23
|
return (
|
|
@@ -22,6 +25,7 @@ export function AgentScreen() {
|
|
|
22
25
|
conversation={conversation}
|
|
23
26
|
locale="es-US"
|
|
24
27
|
theme={{ light: { primary: '#7A2E8E', personBubble: '#7A2E8E' } }}
|
|
28
|
+
components={components}
|
|
25
29
|
onProposal={(card, decision) => api.decideProposal(card, decision)} // your backend, the person's token
|
|
26
30
|
onOpenLink={(link) => router.push(link)}
|
|
27
31
|
/>
|
|
@@ -65,6 +69,12 @@ kit share one copy of `@vinaverse/client`.
|
|
|
65
69
|
- **Background**: the kit tells the conversation when the app goes to the background and comes back (`AppState`).
|
|
66
70
|
By default the connection stays, without pings; `createConversation({ closeInBackground: true, refreshSecret })`
|
|
67
71
|
closes it in the background and opens it again, with a fresh secret, in the foreground. Nothing typed is lost.
|
|
72
|
+
- **A session's end**: pass `expires_at` on from the session your backend created. Past it (the tenant's tool token
|
|
73
|
+
with it), the person's next message gets a new session for the same conversation from `getSession`; the old one is
|
|
74
|
+
never reopened with a fresh secret. A reconnect keeps everything drawn: the session's transcript merges by id.
|
|
75
|
+
- **Modes**: a customer and a professional each get their own conversation, `createConversationsByMode`, and the
|
|
76
|
+
screen draws `conversations.select(mode)`. Each mode keeps its own history on the device, and `getSession` gets the
|
|
77
|
+
`mode` so your backend creates the session for that mode's agent.
|
|
68
78
|
- **Language**: the agent answers in `subject.locale`, which your backend sets when it creates the session; the kit's
|
|
69
79
|
`locale` prop is only for its own words, dates and money (and is passed to `getSession` as a hint you may ignore).
|
|
70
80
|
|
|
@@ -76,8 +86,53 @@ kit share one copy of `@vinaverse/client`.
|
|
|
76
86
|
| `onOpenLink(link, card)` | a card's link (it already passed the deep-link rule): route by the page it names |
|
|
77
87
|
| `onCardAction(card, action)` | a card action without a link (also sent to the session) |
|
|
78
88
|
| `onPickPhoto(question, card)` | a photo question: pick, upload to your backend, return `{ upload_ref }` |
|
|
89
|
+
| `failureAction` | `{ label?, onPress(state) }`: a way out on a failed turn and on "couldn't connect" ([When the agent fails](#when-the-agent-fails)) |
|
|
79
90
|
| `onWarning(message)` | development warnings: a hard-to-read theme, copy that says "assistant" |
|
|
80
91
|
|
|
92
|
+
## When the agent fails
|
|
93
|
+
|
|
94
|
+
Two failures need a way out for the person: a turn that failed before any reply ("Something went wrong.") and no
|
|
95
|
+
session to talk to ("Couldn't connect.", with "Try again"). Give one action for both, drawn as a button by the kit's
|
|
96
|
+
own parts, so a person is always the way out:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<VinaverseChat
|
|
100
|
+
conversation={conversation}
|
|
101
|
+
failureAction={{ onPress: (state) => openSupport(state.conversationId) }} // "Talk to a person", in the person's language
|
|
102
|
+
/>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`label` replaces "Talk to a person" (the copy's `talkToPerson`, which `copy` can also change). Or replace either part:
|
|
106
|
+
`components.Error` (the failed turn) and `components.Unavailable` (the banner) each get the `state` and the kit's own
|
|
107
|
+
part as `Default`. With neither, the kit draws both as before.
|
|
108
|
+
|
|
109
|
+
| `state` | |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `kind` | `turn_failed` or `unavailable` |
|
|
112
|
+
| `reason` | the turn's error code (`internal`, `timeout`, …), or `unavailable` / `unauthorized` |
|
|
113
|
+
| `retry` | try again, when it can be: the person's text once more, for a turn the server allows to retry (once); a new session for `unavailable`. Undefined otherwise |
|
|
114
|
+
| `conversationId` | the conversation, once a session named it: for your support records |
|
|
115
|
+
|
|
116
|
+
## Listings, carousels, links and the welcome
|
|
117
|
+
|
|
118
|
+
- **Professionals and spaces** draw as photo cards: the hero photo (through your `Image` slot, so `expo-image`'s cache
|
|
119
|
+
applies), the name's monogram when there is none or it fails, rating with its count, "From $45" and the next open
|
|
120
|
+
time.
|
|
121
|
+
- **A carousel** for the cards a turn draws together: add the kit's `CardCarousel`. Without it they draw one under
|
|
122
|
+
another; it is opt-in so an app that does not use it does not ship it.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { CardCarousel, VinaverseChat } from '@vinaverse/react-native';
|
|
126
|
+
const components = { Image: ExpoImage, CardGroup: CardCarousel }; // module level: a stable object
|
|
127
|
+
<VinaverseChat conversation={conversation} components={components} onOpenLink={openLink} />;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- **Links**: when your scope keeps the agent from answering, it shows your own help, support, policy, contact or
|
|
131
|
+
feedback pages. Each opens through `onOpenLink` (an https page too: your in-app browser), a deep link only when it
|
|
132
|
+
passes your deep-link prefixes.
|
|
133
|
+
- **The welcome**: pass `welcome` on from the session your backend created; the kit greets an empty conversation with
|
|
134
|
+
it and its chips (no model call). A chip sends its message as the person's own. An `EmptyState` slot gets both.
|
|
135
|
+
|
|
81
136
|
## The keyboard
|
|
82
137
|
|
|
83
138
|
`keyboardBehavior` keeps the composer above the keyboard: `padding` on iOS by default, nothing on Android (the window
|