@giovchat/react-native 0.0.0-stage → 0.2.0

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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -2
  3. package/dist/GiovChat.d.ts +100 -0
  4. package/dist/GiovChat.d.ts.map +1 -0
  5. package/dist/GiovChat.js +257 -0
  6. package/dist/GiovChat.js.map +1 -0
  7. package/dist/attachments.d.ts +121 -0
  8. package/dist/attachments.d.ts.map +1 -0
  9. package/dist/attachments.js +185 -0
  10. package/dist/attachments.js.map +1 -0
  11. package/dist/brand-assets.d.ts +6 -0
  12. package/dist/brand-assets.d.ts.map +1 -0
  13. package/dist/brand-assets.js +9 -0
  14. package/dist/brand-assets.js.map +1 -0
  15. package/dist/client.d.ts +190 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.js +801 -0
  18. package/dist/client.js.map +1 -0
  19. package/dist/color.d.ts +25 -0
  20. package/dist/color.d.ts.map +1 -0
  21. package/dist/color.js +78 -0
  22. package/dist/color.js.map +1 -0
  23. package/dist/components/Attachments.d.ts +13 -0
  24. package/dist/components/Attachments.d.ts.map +1 -0
  25. package/dist/components/Attachments.js +42 -0
  26. package/dist/components/Attachments.js.map +1 -0
  27. package/dist/components/Branding.d.ts +6 -0
  28. package/dist/components/Branding.d.ts.map +1 -0
  29. package/dist/components/Branding.js +28 -0
  30. package/dist/components/Branding.js.map +1 -0
  31. package/dist/components/ChatMarkdown.d.ts +15 -0
  32. package/dist/components/ChatMarkdown.d.ts.map +1 -0
  33. package/dist/components/ChatMarkdown.js +72 -0
  34. package/dist/components/ChatMarkdown.js.map +1 -0
  35. package/dist/components/GiovBotMark.d.ts +6 -0
  36. package/dist/components/GiovBotMark.d.ts.map +1 -0
  37. package/dist/components/GiovBotMark.js +11 -0
  38. package/dist/components/GiovBotMark.js.map +1 -0
  39. package/dist/components/GreetingBubble.d.ts +7 -0
  40. package/dist/components/GreetingBubble.d.ts.map +1 -0
  41. package/dist/components/GreetingBubble.js +12 -0
  42. package/dist/components/GreetingBubble.js.map +1 -0
  43. package/dist/components/Header.d.ts +18 -0
  44. package/dist/components/Header.d.ts.map +1 -0
  45. package/dist/components/Header.js +67 -0
  46. package/dist/components/Header.js.map +1 -0
  47. package/dist/components/MessageBubble.d.ts +16 -0
  48. package/dist/components/MessageBubble.d.ts.map +1 -0
  49. package/dist/components/MessageBubble.js +49 -0
  50. package/dist/components/MessageBubble.js.map +1 -0
  51. package/dist/components/PreChatForm.d.ts +11 -0
  52. package/dist/components/PreChatForm.d.ts.map +1 -0
  53. package/dist/components/PreChatForm.js +72 -0
  54. package/dist/components/PreChatForm.js.map +1 -0
  55. package/dist/components/TypingIndicator.d.ts +9 -0
  56. package/dist/components/TypingIndicator.d.ts.map +1 -0
  57. package/dist/components/TypingIndicator.js +18 -0
  58. package/dist/components/TypingIndicator.js.map +1 -0
  59. package/dist/components/WidgetStyles.d.ts +3 -0
  60. package/dist/components/WidgetStyles.d.ts.map +1 -0
  61. package/dist/components/WidgetStyles.js +9 -0
  62. package/dist/components/WidgetStyles.js.map +1 -0
  63. package/dist/components/bubbleStyles.d.ts +47 -0
  64. package/dist/components/bubbleStyles.d.ts.map +1 -0
  65. package/dist/components/bubbleStyles.js +19 -0
  66. package/dist/components/bubbleStyles.js.map +1 -0
  67. package/dist/components/index.d.ts +11 -0
  68. package/dist/components/index.d.ts.map +1 -0
  69. package/dist/components/index.js +14 -0
  70. package/dist/components/index.js.map +1 -0
  71. package/dist/errors.d.ts +18 -0
  72. package/dist/errors.d.ts.map +1 -0
  73. package/dist/errors.js +26 -0
  74. package/dist/errors.js.map +1 -0
  75. package/dist/i18n.d.ts +73 -0
  76. package/dist/i18n.d.ts.map +1 -0
  77. package/dist/i18n.js +292 -0
  78. package/dist/i18n.js.map +1 -0
  79. package/dist/identity.d.ts +41 -0
  80. package/dist/identity.d.ts.map +1 -0
  81. package/dist/identity.js +41 -0
  82. package/dist/identity.js.map +1 -0
  83. package/dist/index.d.ts +20 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +46 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/markdown.d.ts +56 -0
  88. package/dist/markdown.d.ts.map +1 -0
  89. package/dist/markdown.js +219 -0
  90. package/dist/markdown.js.map +1 -0
  91. package/dist/outbox.d.ts +68 -0
  92. package/dist/outbox.d.ts.map +1 -0
  93. package/dist/outbox.js +168 -0
  94. package/dist/outbox.js.map +1 -0
  95. package/dist/protocol.d.ts +9 -0
  96. package/dist/protocol.d.ts.map +1 -0
  97. package/dist/protocol.js +34 -0
  98. package/dist/protocol.js.map +1 -0
  99. package/dist/styleOverrides.d.ts +37 -0
  100. package/dist/styleOverrides.d.ts.map +1 -0
  101. package/dist/styleOverrides.js +54 -0
  102. package/dist/styleOverrides.js.map +1 -0
  103. package/dist/types.d.ts +111 -0
  104. package/dist/types.d.ts.map +1 -0
  105. package/dist/types.js +3 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/unread.d.ts +9 -0
  108. package/dist/unread.d.ts.map +1 -0
  109. package/dist/unread.js +49 -0
  110. package/dist/unread.js.map +1 -0
  111. package/dist/useChatSession.d.ts +78 -0
  112. package/dist/useChatSession.d.ts.map +1 -0
  113. package/dist/useChatSession.js +796 -0
  114. package/dist/useChatSession.js.map +1 -0
  115. package/package.json +59 -4
  116. package/src/GiovChat.tsx +530 -0
  117. package/src/attachments.ts +218 -0
  118. package/src/brand-assets.ts +5 -0
  119. package/src/client.ts +758 -0
  120. package/src/color.ts +74 -0
  121. package/src/components/Attachments.tsx +75 -0
  122. package/src/components/Branding.tsx +35 -0
  123. package/src/components/ChatMarkdown.tsx +111 -0
  124. package/src/components/GiovBotMark.tsx +19 -0
  125. package/src/components/GreetingBubble.tsx +20 -0
  126. package/src/components/Header.tsx +131 -0
  127. package/src/components/MessageBubble.tsx +121 -0
  128. package/src/components/PreChatForm.tsx +183 -0
  129. package/src/components/TypingIndicator.tsx +34 -0
  130. package/src/components/WidgetStyles.tsx +5 -0
  131. package/src/components/bubbleStyles.ts +16 -0
  132. package/src/components/index.ts +14 -0
  133. package/src/errors.ts +30 -0
  134. package/src/i18n.ts +351 -0
  135. package/src/identity.ts +54 -0
  136. package/src/index.ts +68 -0
  137. package/src/markdown.ts +227 -0
  138. package/src/outbox.ts +175 -0
  139. package/src/protocol.ts +31 -0
  140. package/src/styleOverrides.ts +85 -0
  141. package/src/types.ts +119 -0
  142. package/src/unread.ts +39 -0
  143. package/src/useChatSession.ts +695 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fegig Tech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,334 @@
1
- # Temporary Holding Version
1
+ # @giovchat/react-native
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ React Native SDK for the GiovChat live chat platform: the same conversation your website visitors have with the web widget, as a native screen in your app. Widget settings (colours, greeting, pre-chat fields, GiovBot name and avatar, attachment policy) are managed in the **Agent Dashboard**; the SDK fetches them with your tenant key.
4
+
5
+ What it does, matching the web widget:
6
+
7
+ - Replies stream in as GiovBot writes them, formatted with the same Chat Markdown (bold, lists, links, code, quotes).
8
+ - The header shows who is answering: GiovBot (its name and avatar) while it answers, the teammate's name and photo once a person takes over, otherwise your team.
9
+ - Messages typed offline or while reconnecting wait in an outbox and go out in order once the connection is back. Each one is sent under one id, so it is never stored twice. Failures show **Not sent · Tap to retry**.
10
+ - After a dropped connection or when the app returns to the foreground, the chat reconnects and catches up on anything it missed.
11
+ - Typing indicators both ways, attachments with upload progress and previews, an unread count while the chat is closed.
12
+ - Interface text in English, French, Spanish, Portuguese and German, following the device language.
13
+ - Light, dark or automatic theme.
14
+
15
+ No native modules are required. Optional capabilities (file and image picking, push notifications) use your app's own libraries through small adapters.
16
+
17
+ The SDK implements the Giov visitor protocol v1: its markdown passes every conformance vector, and its connection, catch-up, outbox and identity rules follow the protocol guide.
18
+
19
+ Full documentation, including the protocol, is at **https://giovchat.app/docs**.
20
+
21
+ A conversation starts when the visitor sends their first message (or submits the pre-chat form), so opening the chat screen never creates an empty conversation. A stored conversation is always resumed first; a closed one is forgotten and the next message starts a new one.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ # npm
27
+ npm install @giovchat/react-native react-native-safe-area-context
28
+
29
+ # Expo
30
+ npx expo install @giovchat/react-native react-native-safe-area-context
31
+ ```
32
+
33
+ With pnpm, Yarn or Bun, add the same two packages with `pnpm add`, `yarn add` or `bun add`.
34
+
35
+ Wrap your app in `SafeAreaProvider` from `react-native-safe-area-context` if your navigation setup does not already provide one.
36
+
37
+ ## Quick start
38
+
39
+ ### Modal (default)
40
+
41
+ ```tsx
42
+ import { useState } from "react";
43
+ import { Button, View } from "react-native";
44
+ import { GiovChat } from "@giovchat/react-native";
45
+
46
+ export function SupportButton() {
47
+ const [open, setOpen] = useState(false);
48
+ return (
49
+ <View>
50
+ <Button title="Chat with us" onPress={() => setOpen(true)} />
51
+ <GiovChat tenantKey="YOUR_TENANT_KEY" visible={open} onClose={() => setOpen(false)} />
52
+ </View>
53
+ );
54
+ }
55
+ ```
56
+
57
+ ### Fullscreen
58
+
59
+ ```tsx
60
+ <GiovChat tenantKey="YOUR_TENANT_KEY" mode="fullscreen" />
61
+ ```
62
+
63
+ ## Signed-in users
64
+
65
+ Pass the user's details through `contact`. Any field you provide skips the matching pre-chat field.
66
+
67
+ ```tsx
68
+ <GiovChat
69
+ tenantKey="YOUR_TENANT_KEY"
70
+ contact={{
71
+ userId: currentUser.id,
72
+ userHash: currentUser.giovUserHash, // from your server, see below
73
+ email: currentUser.email,
74
+ firstName: currentUser.firstName,
75
+ }}
76
+ sessionKey={currentUser.id}
77
+ />
78
+ ```
79
+
80
+ - `userHash` is the HMAC-SHA256 (hex) of `userId` with the visitor identity secret from **Settings → Chat widget → Visitor details**. Compute it on your server and send it to the app; never ship the secret. Without `userHash`, `userId` is recorded as an unverified claim and does not link the chat to an existing contact.
81
+ - **Logout and account switching:** change `sessionKey` (or call `ref.current.reset()`). The previous identity's session, queued messages and unread count are dropped and the next user starts fresh.
82
+
83
+ ### Keeping the conversation across launches
84
+
85
+ By default nothing is persisted. Pass `sessionStorage` to resume the same conversation next time, using secure storage you already have. The `scope` it receives is made from the API, the tenant key, `sessionKey` and `userId`, so editing the visitor's name, email or phone keeps the conversation:
86
+
87
+ ```tsx
88
+ import * as SecureStore from "expo-secure-store";
89
+ import type { GiovChatSessionStorage } from "@giovchat/react-native";
90
+
91
+ const key = (scope: string) => "giovchat." + scope.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 200);
92
+
93
+ export const chatStorage: GiovChatSessionStorage = {
94
+ async load(scope) { const value = await SecureStore.getItemAsync(key(scope)); return value ? JSON.parse(value) : null; },
95
+ async save(scope, session) { await SecureStore.setItemAsync(key(scope), JSON.stringify(session)); },
96
+ async remove(scope) { await SecureStore.deleteItemAsync(key(scope)); },
97
+ };
98
+ ```
99
+
100
+ The session token is a private credential. Keep it in secure storage, never log it, and never share it between users.
101
+
102
+ ## Attachments
103
+
104
+ Attachments appear once the conversation has started, when you pass an `attachmentPicker` **and** the published GiovBot agent allows them (Dashboard → GiovBot → agent settings). Visitors can pick only the types and sizes the agent allows, up to 5 files per message; the server checks again. Images show inline with their shape reserved; other files open in the browser through an authenticated link.
105
+
106
+ The picker returns `{ uri, name, type, size?, width?, height?, thumbnail? }` objects, or `[]` when the visitor cancels.
107
+
108
+ ### Expo: documents and images
109
+
110
+ ```tsx
111
+ import * as DocumentPicker from "expo-document-picker";
112
+ import type { GiovChatAttachmentPicker } from "@giovchat/react-native";
113
+
114
+ export const documentPicker: GiovChatAttachmentPicker = {
115
+ async pick({ allowedTypes }) {
116
+ const result = await DocumentPicker.getDocumentAsync({ type: allowedTypes, multiple: true, copyToCacheDirectory: true });
117
+ if (result.canceled) return [];
118
+ return result.assets.map((asset) => ({
119
+ uri: asset.uri, name: asset.name, type: asset.mimeType ?? "application/octet-stream", size: asset.size,
120
+ }));
121
+ },
122
+ };
123
+ ```
124
+
125
+ ```tsx
126
+ import * as ImagePicker from "expo-image-picker";
127
+ import type { GiovChatAttachmentPicker } from "@giovchat/react-native";
128
+
129
+ export const photoPicker: GiovChatAttachmentPicker = {
130
+ async pick({ maxFiles }) {
131
+ const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ["images"], allowsMultipleSelection: true, selectionLimit: maxFiles, quality: 0.85 });
132
+ if (result.canceled) return [];
133
+ return result.assets.map((asset) => ({
134
+ uri: asset.uri, name: asset.fileName ?? "photo.jpg", type: asset.mimeType ?? "image/jpeg",
135
+ size: asset.fileSize, width: asset.width, height: asset.height,
136
+ }));
137
+ },
138
+ };
139
+ ```
140
+
141
+ To let every viewer (including your agents) see an image's shape before it loads, add a small thumbnail, e.g. with `expo-image-manipulator`: resize to about 24px wide, save as JPEG with `base64: true`, and pass `thumbnail: "data:image/jpeg;base64," + base64`. It must be under 6,000 characters; otherwise it is left out and the image simply loads in place.
142
+
143
+ ### Bare React Native
144
+
145
+ ```tsx
146
+ import { pick, types } from "@react-native-documents/picker";
147
+ import type { GiovChatAttachmentPicker } from "@giovchat/react-native";
148
+
149
+ export const filePicker: GiovChatAttachmentPicker = {
150
+ async pick({ allowedTypes }) {
151
+ try {
152
+ const files = await pick({ allowMultiSelection: true, type: allowedTypes.length ? allowedTypes : [types.allFiles] });
153
+ return files.map((file) => ({ uri: file.uri, name: file.name ?? "file", type: file.type ?? "application/octet-stream", size: file.size ?? undefined }));
154
+ } catch {
155
+ return []; // Cancelled.
156
+ }
157
+ },
158
+ };
159
+ ```
160
+
161
+ Then: `<GiovChat tenantKey="…" attachmentPicker={documentPicker} />`. Uploads report progress in the composer.
162
+
163
+ ## Unread replies and badges
164
+
165
+ By default the chat connects only while it is visible. To count replies that arrive while it is closed, keep it mounted with `keepConnected`. It only resumes an existing conversation and never starts one in the background.
166
+
167
+ ```tsx
168
+ import { GiovChat, useGiovChatUnreadCount } from "@giovchat/react-native";
169
+
170
+ function ChatTabIcon() {
171
+ const unread = useGiovChatUnreadCount("YOUR_TENANT_KEY");
172
+ return <Icon name="chat" badge={unread || undefined} />;
173
+ }
174
+
175
+ <GiovChat tenantKey="YOUR_TENANT_KEY" visible={open} keepConnected sessionStorage={chatStorage}
176
+ onUnreadCountChange={(count) => console.log(count)} />;
177
+ ```
178
+
179
+ Opening the chat clears the count.
180
+
181
+ ## Push notifications
182
+
183
+ ```tsx
184
+ const chat = useRef<GiovChatHandle>(null);
185
+ // After your app obtained an APNs or FCM token (e.g. expo-notifications getDevicePushTokenAsync):
186
+ const result = await chat.current?.registerPushToken(deviceToken, Platform.OS === "ios" ? "ios" : "android", {
187
+ environment: __DEV__ ? "sandbox" : "production", // APNs only: development builds get sandbox tokens
188
+ });
189
+ ```
190
+
191
+ Your app then gets a notification when the team or GiovBot replies while the chat is not open. Add your Apple Push key and/or Firebase service account in the Giov dashboard under **Settings → Chat widget → Push notifications**; notifications are sent with your keys, from your app. Pass the raw device token: the APNs token on iOS (or the FCM token if your iOS app uses Firebase Messaging), the FCM token on Android. A tapped notification carries `{ type: "giov.message", tenantId, conversationId, messageId }` (under `giov` on APNs, in `data` on FCM); open the chat and it catches up from history.
192
+
193
+ It registers immediately when connected, otherwise as soon as the conversation connects, and never throws. Against a server without visitor push it resolves `{ registered: false, reason: "unsupported" }` after one probe and makes no further requests. The device language (or the `locale` prop) goes along, so the generic text a workspace can use instead of message text is in the visitor's language; pass `{ locale }` to choose another.
194
+
195
+ Called before the visitor has written, the registration waits for the conversation (the first message starts it) and resolves `{ registered: false, reason: "no_conversation" }` if the chat unmounts (or `sessionKey` changes), on `reset()`, or when the workspace refuses the visitor's identity. The token is remembered: when the team closes a conversation and the visitor starts a new one, the device is registered for it automatically.
196
+
197
+ When the visitor signs out or turns notifications off, stop them before resetting the chat (this also works while the chat is closed):
198
+
199
+ ```tsx
200
+ await chat.current?.unregisterPushToken(deviceToken);
201
+ await chat.current?.reset();
202
+ ```
203
+
204
+ The socket stays open for 5 seconds after the app goes to the background (a file picker or a glance at the app switcher does not reload the chat), then closes so replies arrive as notifications; the chat reconnects and catches up when the app returns.
205
+
206
+ ## Language
207
+
208
+ The interface follows the device language (English, French, Spanish, Portuguese and German; others use English). Text the business writes in the dashboard (greeting, placeholder, bot name) is shown as written.
209
+
210
+ ```tsx
211
+ <GiovChat tenantKey="…" locale="pt-BR" strings={{ sendMessage: "Enviar", startChat: "Começar" }} />
212
+ ```
213
+
214
+ Any string can be replaced through `strings`; see `GiovChatStrings` for the keys. `GIOVCHAT_STRINGS` exports the built-in tables.
215
+
216
+ ## Appearance
217
+
218
+ - **Theme:** the dashboard setting (`light`, `dark`, `auto`) applies; `auto` follows the device. Override it with `theme`.
219
+ - **Colour:** the dashboard's primary colour applies; override it with `primaryColor`.
220
+ - **Fonts and colours of each surface:** `styleOverrides`.
221
+
222
+ ```tsx
223
+ import { GiovChat, type GiovChatStyleOverrides } from "@giovchat/react-native";
224
+
225
+ const chatStyles = {
226
+ fontFamily: "YourAppFont", // Load/register this font in your app first.
227
+ container: { backgroundColor: "#f8fafc" },
228
+ header: { backgroundColor: "#0f172a" },
229
+ headerTitle: { color: "#ffffff", fontWeight: "700" },
230
+ headerSubtitle: { color: "#cbd5e1" },
231
+ agentBubble: { backgroundColor: "#e2e8f0" },
232
+ agentText: { color: "#0f172a" },
233
+ visitorBubble: { backgroundColor: "#2563eb" },
234
+ visitorText: { color: "#ffffff" },
235
+ composer: { backgroundColor: "#f8fafc" },
236
+ input: { backgroundColor: "#ffffff", color: "#0f172a" },
237
+ button: { backgroundColor: "#2563eb" },
238
+ buttonText: { color: "#ffffff" },
239
+ } satisfies GiovChatStyleOverrides;
240
+
241
+ <GiovChat tenantKey="YOUR_TENANT_KEY" styleOverrides={chatStyles} />;
242
+ ```
243
+
244
+ | Slots | Allowed properties | Applies to |
245
+ |---|---|---|
246
+ | `container`, `header`, `composer`, `form` | `backgroundColor` | Chat surface, header, composer row, pre-chat form |
247
+ | `agentBubble`, `visitorBubble` | `backgroundColor` | Agent/greeting/typing bubbles and visitor bubbles |
248
+ | `button` | `backgroundColor` | Send, start-chat and retry buttons |
249
+ | `headerTitle`, `headerSubtitle` | `fontFamily`, `fontWeight`, `fontStyle`, `color` | Header text; title colour also applies to the close icon |
250
+ | `agentText`, `visitorText` | Same text properties | Message content (every formatted run), sender labels and timestamps; agent text also covers greeting and typing |
251
+ | `formTitle`, `label`, `buttonText`, `hint`, `errorText` | Same text properties | Form heading and labels, button text, loading hint, attachment chips, errors; hint colour also controls input placeholders |
252
+ | `input` | Text properties plus `backgroundColor` | Composer and all pre-chat inputs |
253
+
254
+ The top-level `fontFamily` is the default for customizable text; a slot's `fontFamily` takes precedence. Colours must be React Native colour strings; use readable foreground/background combinations. Registered style IDs, style arrays, unsupported slots and unsupported properties are ignored at runtime, including for JavaScript callers. Font sizes, spacing, dimensions, borders, positioning, visibility, opacity and the branding footer remain SDK-controlled.
255
+
256
+ ### Branding and plans
257
+
258
+ The widget reads `showBranding` from the server's `/widget/config` response. Starter and Pro always display **Powered by GiovChat**; higher plans can remove it in the dashboard. Branding stays visible when the field is missing or configuration cannot be loaded; only an explicit server `false` hides it. Its font, colours and background are isolated from all style overrides. Headless integrations render their own UI and must honour the server's branding policy.
259
+
260
+ ## Props
261
+
262
+ | Prop | Type | Default | Description |
263
+ |---|---|---|---|
264
+ | `tenantKey` | `string` | **required** | Tenant key from your agent dashboard |
265
+ | `mode` | `"modal" \| "fullscreen"` | `"modal"` | Modal sheet or fill the parent view |
266
+ | `visible` | `boolean` | `true` | Show or hide the chat |
267
+ | `onClose` | `() => void` | — | Called when the visitor taps close |
268
+ | `contact` | `GiovChatContact` | — | Signed-in user's details; see above |
269
+ | `sessionKey` | `string \| number` | — | Change it to isolate or reset the session (logout, account switch) |
270
+ | `sessionStorage` | `GiovChatSessionStorage` | none | Host-owned secure persistence of the session |
271
+ | `baseUrl` | `string` | `https://api.giovchat.app` | API base URL override |
272
+ | `primaryColor` | `string` | From dashboard | Brand colour override |
273
+ | `theme` | `"light" \| "dark" \| "auto"` | From dashboard | Theme override; `auto` follows the device |
274
+ | `styleOverrides` | `GiovChatStyleOverrides` | none | Allowlisted font and colour overrides |
275
+ | `locale` | `string` | Device language | Interface language |
276
+ | `strings` | `Partial<GiovChatStrings>` | none | Replace any interface string |
277
+ | `attachmentPicker` | `GiovChatAttachmentPicker` | none | Enables attachments (when the agent allows them) |
278
+ | `keepConnected` | `boolean` | `false` | Stay connected while hidden to count unread replies |
279
+ | `onUnreadCountChange` | `(count: number) => void` | — | Unread agent replies while hidden |
280
+ | `onConversationStart` | `(id: string) => void` | — | Called once with the conversation ID |
281
+ | `onError` | `(error: GiovChatError) => void` | — | Every failure, with a `code` |
282
+ | `onConnectionStatusChange` | `(status) => void` | — | `"connecting" \| "connected" \| "disconnected"` |
283
+
284
+ `ref` (`GiovChatHandle`): `reset()` clears this identity's session and starts fresh; `registerPushToken(token, platform, options?)` and `unregisterPushToken(token)` manage push (see above).
285
+
286
+ `onError` codes: `network`, `timeout`, `invalid_response`, `invalid_session` (the stored session is gone), `conversation_closed`, `connection_failed`, `disconnected`, `send_failed`, `upload_failed`, `workspace_busy`, `identity_rejected` (the workspace requires a signed identity and `userHash` is wrong or missing), `storage`, `server`, `rate_limited` (with `retryAfterSeconds`).
287
+
288
+ ## Headless usage
289
+
290
+ For a fully custom UI, the transport and helpers are exported. `ChatClient` owns the WebSocket; pass it an `Outbox` that outlives it if you reconnect with new clients.
291
+
292
+ ```ts
293
+ import { ChatClient, Outbox, createConversation, fetchConversationHistory, fetchWidgetConfig, DEFAULT_BASE_URL } from "@giovchat/react-native";
294
+
295
+ const tenantKey = "YOUR_TENANT_KEY";
296
+ const config = await fetchWidgetConfig(DEFAULT_BASE_URL, tenantKey);
297
+ const session = await createConversation(DEFAULT_BASE_URL, tenantKey, { userId: "user-123", userHash });
298
+
299
+ const outbox = new Outbox();
300
+ const client = new ChatClient({ tenantKey, outbox, flushOnOpen: false });
301
+ client.onMessage((message) => render(message)); // agent replies (Chat Markdown in message.content)
302
+ client.onAck((clientMessageId, saved) => replace(clientMessageId, saved));
303
+ client.onStream((event) => event.end ? dropDraft(event.streamId) : showDraft(event.streamId, event.content));
304
+ client.onTyping((typing, isBot) => showTyping(typing, isBot));
305
+ // Catch-up after every (re)connect, in the protocol's order: history, merge by id, then resend the outbox.
306
+ client.onStatusChange((connected) => connected && fetchConversationHistory(DEFAULT_BASE_URL, tenantKey, session)
307
+ .then((history) => { catchUp(history); client.flush(); }));
308
+ outbox.subscribe(() => renderPending(outbox.list())); // "sending" / "failed" messages
309
+
310
+ await client.connect(session);
311
+ client.send("Hello!"); // queued if not connected; sent in order once it is
312
+ client.sendTyping(); client.sendTypingEnd();
313
+ const upload = await client.uploadAttachment({ uri, name, type }, (progress) => {});
314
+ client.send("", [upload.url]);
315
+ client.retryMessage(failedId);
316
+ client.disconnect();
317
+ ```
318
+
319
+ - `parseChatMarkdown(text)` returns the formatted tree the built-in UI renders.
320
+ - `headerIdentity(config, agentName, agentAvatar)` gives the header rule.
321
+ - On typing: `isBot` is `true` for AI, `false` for a person and `undefined` for an older sender. Never infer AI typing from `config.botEnabled`.
322
+ - Headless integrations must honour the server's branding policy.
323
+
324
+ ## Upgrading from 0.1
325
+
326
+ See [CHANGELOG.md](./CHANGELOG.md). The props and exports of 0.1 keep working. Behaviour changes: the conversation now starts with the visitor's first message instead of when the chat opens; a closed conversation is forgotten and the next message starts a new one; and `ChatClient.sendMessage()` waits in the outbox while the connection is being restored instead of failing at once with `disconnected`.
327
+
328
+ ## Support
329
+
330
+ Documentation: https://giovchat.app/docs. Bugs and feature requests: https://github.com/Fegig-Tech/giovchat-react-native/issues.
331
+
332
+ ## License
333
+
334
+ MIT © Fegig Tech. See [LICENSE](./LICENSE).
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import type { GiovChatError } from "./errors";
3
+ import type { GiovChatContact, GiovChatConnectionStatus, GiovChatSessionStorage } from "./types";
4
+ import { type GiovChatStyleOverrides } from "./styleOverrides";
5
+ import { type GiovChatStrings } from "./i18n";
6
+ import { type GiovChatAttachmentPicker } from "./attachments";
7
+ import type { GiovPushOptions, GiovPushPlatform, GiovPushRegistration } from "./client";
8
+ export interface GiovChatProps {
9
+ /**
10
+ * Tenant key from your agent dashboard.
11
+ * Widget settings (colors, greeting, pre-chat fields, bot name, etc.) are
12
+ * fetched from the server automatically using this key.
13
+ */
14
+ tenantKey: string;
15
+ /**
16
+ * Render mode:
17
+ * - `"modal"` (default) — wraps the chat in a native `Modal` sheet.
18
+ * Control visibility with the `visible` prop.
19
+ * - `"fullscreen"` — renders inline, filling the parent container.
20
+ * Use inside a dedicated screen or tab.
21
+ */
22
+ mode?: "modal" | "fullscreen";
23
+ /**
24
+ * Show or hide the chat.
25
+ * In `modal` mode this drives `Modal.visible`.
26
+ * In `fullscreen` mode the component renders `null` when `false`.
27
+ * @default true
28
+ */
29
+ visible?: boolean;
30
+ /** Called when the user taps the close (✕) button. */
31
+ onClose?: () => void;
32
+ /**
33
+ * Contact info for the currently logged-in user.
34
+ * Any field supplied here is forwarded to the server automatically
35
+ * and skips the corresponding pre-chat form field.
36
+ */
37
+ contact?: GiovChatContact;
38
+ /**
39
+ * Override the API base URL.
40
+ * @default "https://api.giovchat.app"
41
+ */
42
+ baseUrl?: string;
43
+ /**
44
+ * Override the primary brand color from the dashboard.
45
+ * Use a React Native color string.
46
+ */
47
+ primaryColor?: string;
48
+ /** Override the dashboard's theme. `"auto"` follows the device. */
49
+ theme?: "light" | "dark" | "auto";
50
+ /** Allowed font and color overrides. Layout and plan branding remain SDK-controlled. */
51
+ styleOverrides?: GiovChatStyleOverrides;
52
+ /** Interface language ("fr", "pt-BR", …). Defaults to the device language; unsupported ones use English. */
53
+ locale?: string;
54
+ /** Replace any of the SDK's interface strings. */
55
+ strings?: Partial<GiovChatStrings>;
56
+ /**
57
+ * Enables attachments: the host's own file or image picker. Visitors can attach only what the
58
+ * agent allows; without a picker the attach button is hidden.
59
+ */
60
+ attachmentPicker?: GiovChatAttachmentPicker;
61
+ /**
62
+ * Stay connected while the chat is hidden (and the app is in the foreground) so new replies are
63
+ * counted as unread. Only resumes an existing conversation; it never starts one.
64
+ */
65
+ keepConnected?: boolean;
66
+ /** Agent replies the visitor has not seen because the chat was hidden. */
67
+ onUnreadCountChange?: (count: number) => void;
68
+ /** Invoked once with the conversation ID after a session is created. */
69
+ onConversationStart?: (conversationId: string) => void;
70
+ /** Changes isolate/reset the session, for example on logout or account switching. */
71
+ sessionKey?: string | number;
72
+ /** Optional secure storage owned by the host application. No persistence by default. */
73
+ sessionStorage?: GiovChatSessionStorage;
74
+ onError?: (error: GiovChatError) => void;
75
+ onConnectionStatusChange?: (status: GiovChatConnectionStatus) => void;
76
+ }
77
+ export interface GiovChatHandle {
78
+ /** Clears this identity's stored session and starts fresh if the chat is visible. */
79
+ reset(): Promise<void>;
80
+ /**
81
+ * Registers the device's push token (APNs or FCM) for replies in this conversation, now or once
82
+ * it connects. Pass `{ environment: "sandbox" }` from iOS development builds. Resolves
83
+ * `{ registered: false, reason: "unsupported" }` against a server without visitor push. The
84
+ * notification language defaults to the `locale` prop, else the device language.
85
+ *
86
+ * Before any conversation exists the promise stays pending until one connects (the visitor's
87
+ * first message starts it). It resolves `{ registered: false, reason: "no_conversation" }` when
88
+ * the chat unmounts (including a `sessionKey` change), on `reset()`, or when the workspace refuses
89
+ * the visitor's identity. The token is remembered: a new conversation after the team closed the
90
+ * last one is registered again automatically, until `reset()` or `unregisterPushToken()`.
91
+ */
92
+ registerPushToken(token: string, platform: GiovPushPlatform, options?: GiovPushOptions): Promise<GiovPushRegistration>;
93
+ /**
94
+ * Stops push notifications about this conversation on this device (sign-out, notifications turned
95
+ * off). Call it before `reset()`. Resolves true when the server confirmed; never throws.
96
+ */
97
+ unregisterPushToken(token: string): Promise<boolean>;
98
+ }
99
+ export declare const GiovChat: React.ForwardRefExoticComponent<GiovChatProps & React.RefAttributes<GiovChatHandle>>;
100
+ //# sourceMappingURL=GiovChat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GiovChat.d.ts","sourceRoot":"","sources":["../src/GiovChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiF,MAAM,OAAO,CAAC;AAgBtG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQ9C,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAe,MAAM,SAAS,CAAC;AAE9G,OAAO,EAAyB,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAGtF,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,QAAQ,CAAC;AAKtF,OAAO,EAKL,KAAK,wBAAwB,EAE9B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAKxF,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;IAE9B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAElC,wFAAwF;IACxF,cAAc,CAAC,EAAE,sBAAsB,CAAC;IAExC,4GAA4G;IAC5G,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAEnC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAE5C;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAE9C,wEAAwE;IACxE,mBAAmB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,wFAAwF;IACxF,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACvE;AAED,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACvH;;;OAGG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD;AAID,eAAO,MAAM,QAAQ,sFAGnB,CAAC"}