turbodesk-livechat-react-native 0.1.0-alpha.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.
- package/CHANGELOG.md +30 -0
- package/README.md +91 -0
- package/dist/api/conversation-api.d.ts +16 -0
- package/dist/api/conversation-api.d.ts.map +1 -0
- package/dist/api/conversation-api.js +44 -0
- package/dist/api/conversation-api.js.map +1 -0
- package/dist/api/file-api.d.ts +5 -0
- package/dist/api/file-api.d.ts.map +1 -0
- package/dist/api/file-api.js +15 -0
- package/dist/api/file-api.js.map +1 -0
- package/dist/api/widget-api.d.ts +4 -0
- package/dist/api/widget-api.d.ts.map +1 -0
- package/dist/api/widget-api.js +15 -0
- package/dist/api/widget-api.js.map +1 -0
- package/dist/axios/axios.d.ts +32 -0
- package/dist/axios/axios.d.ts.map +1 -0
- package/dist/axios/axios.js +120 -0
- package/dist/axios/axios.js.map +1 -0
- package/dist/core/config.d.ts +17 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/http-client.d.ts +33 -0
- package/dist/core/http-client.d.ts.map +1 -0
- package/dist/core/http-client.js +104 -0
- package/dist/core/http-client.js.map +1 -0
- package/dist/core/identity.d.ts +7 -0
- package/dist/core/identity.d.ts.map +1 -0
- package/dist/core/identity.js +62 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/visitor-params.d.ts +15 -0
- package/dist/core/visitor-params.d.ts.map +1 -0
- package/dist/core/visitor-params.js +45 -0
- package/dist/core/visitor-params.js.map +1 -0
- package/dist/hooks/use-conversations.d.ts +12 -0
- package/dist/hooks/use-conversations.d.ts.map +1 -0
- package/dist/hooks/use-conversations.js +177 -0
- package/dist/hooks/use-conversations.js.map +1 -0
- package/dist/hooks/use-live-chat.d.ts +30 -0
- package/dist/hooks/use-live-chat.d.ts.map +1 -0
- package/dist/hooks/use-live-chat.js +52 -0
- package/dist/hooks/use-live-chat.js.map +1 -0
- package/dist/hooks/use-messages.d.ts +11 -0
- package/dist/hooks/use-messages.d.ts.map +1 -0
- package/dist/hooks/use-messages.js +185 -0
- package/dist/hooks/use-messages.js.map +1 -0
- package/dist/hooks/use-send-message.d.ts +22 -0
- package/dist/hooks/use-send-message.d.ts.map +1 -0
- package/dist/hooks/use-send-message.js +125 -0
- package/dist/hooks/use-send-message.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation/LiveChatPanel.d.ts +5 -0
- package/dist/navigation/LiveChatPanel.d.ts.map +1 -0
- package/dist/navigation/LiveChatPanel.js +81 -0
- package/dist/navigation/LiveChatPanel.js.map +1 -0
- package/dist/navigation/panel-router-context.d.ts +22 -0
- package/dist/navigation/panel-router-context.d.ts.map +1 -0
- package/dist/navigation/panel-router-context.js +42 -0
- package/dist/navigation/panel-router-context.js.map +1 -0
- package/dist/navigation/router-types.d.ts +2 -0
- package/dist/navigation/router-types.d.ts.map +1 -0
- package/dist/navigation/router-types.js +3 -0
- package/dist/navigation/router-types.js.map +1 -0
- package/dist/provider/LiveChatContext.d.ts +4 -0
- package/dist/provider/LiveChatContext.d.ts.map +1 -0
- package/dist/provider/LiveChatContext.js +35 -0
- package/dist/provider/LiveChatContext.js.map +1 -0
- package/dist/provider/LiveChatProvider.d.ts +3 -0
- package/dist/provider/LiveChatProvider.d.ts.map +1 -0
- package/dist/provider/LiveChatProvider.js +308 -0
- package/dist/provider/LiveChatProvider.js.map +1 -0
- package/dist/provider/types.d.ts +42 -0
- package/dist/provider/types.d.ts.map +1 -0
- package/dist/provider/types.js +3 -0
- package/dist/provider/types.js.map +1 -0
- package/dist/realtime/ws-client.d.ts +51 -0
- package/dist/realtime/ws-client.d.ts.map +1 -0
- package/dist/realtime/ws-client.js +322 -0
- package/dist/realtime/ws-client.js.map +1 -0
- package/dist/ui/components/AssigneeAvatar.d.ts +12 -0
- package/dist/ui/components/AssigneeAvatar.d.ts.map +1 -0
- package/dist/ui/components/AssigneeAvatar.js +58 -0
- package/dist/ui/components/AssigneeAvatar.js.map +1 -0
- package/dist/ui/components/Avatar.d.ts +10 -0
- package/dist/ui/components/Avatar.d.ts.map +1 -0
- package/dist/ui/components/Avatar.js +76 -0
- package/dist/ui/components/Avatar.js.map +1 -0
- package/dist/ui/components/ConversationHeader.d.ts +10 -0
- package/dist/ui/components/ConversationHeader.d.ts.map +1 -0
- package/dist/ui/components/ConversationHeader.js +90 -0
- package/dist/ui/components/ConversationHeader.js.map +1 -0
- package/dist/ui/components/ConversationListScreen.d.ts +9 -0
- package/dist/ui/components/ConversationListScreen.d.ts.map +1 -0
- package/dist/ui/components/ConversationListScreen.js +350 -0
- package/dist/ui/components/ConversationListScreen.js.map +1 -0
- package/dist/ui/components/ConversationScreen.d.ts +8 -0
- package/dist/ui/components/ConversationScreen.d.ts.map +1 -0
- package/dist/ui/components/ConversationScreen.js +235 -0
- package/dist/ui/components/ConversationScreen.js.map +1 -0
- package/dist/ui/components/HomeScreen.d.ts +6 -0
- package/dist/ui/components/HomeScreen.d.ts.map +1 -0
- package/dist/ui/components/HomeScreen.js +133 -0
- package/dist/ui/components/HomeScreen.js.map +1 -0
- package/dist/ui/components/LivechatMessageRenderer.d.ts +17 -0
- package/dist/ui/components/LivechatMessageRenderer.d.ts.map +1 -0
- package/dist/ui/components/LivechatMessageRenderer.js +122 -0
- package/dist/ui/components/LivechatMessageRenderer.js.map +1 -0
- package/dist/ui/components/LogMessage.d.ts +5 -0
- package/dist/ui/components/LogMessage.d.ts.map +1 -0
- package/dist/ui/components/LogMessage.js +83 -0
- package/dist/ui/components/LogMessage.js.map +1 -0
- package/dist/ui/components/MessageBubble.d.ts +15 -0
- package/dist/ui/components/MessageBubble.d.ts.map +1 -0
- package/dist/ui/components/MessageBubble.js +84 -0
- package/dist/ui/components/MessageBubble.js.map +1 -0
- package/dist/ui/components/MessageComposer.d.ts +31 -0
- package/dist/ui/components/MessageComposer.d.ts.map +1 -0
- package/dist/ui/components/MessageComposer.js +295 -0
- package/dist/ui/components/MessageComposer.js.map +1 -0
- package/dist/ui/components/WsStatusStrip.d.ts +2 -0
- package/dist/ui/components/WsStatusStrip.d.ts.map +1 -0
- package/dist/ui/components/WsStatusStrip.js +103 -0
- package/dist/ui/components/WsStatusStrip.js.map +1 -0
- package/dist/ui/icons.d.ts +22 -0
- package/dist/ui/icons.d.ts.map +1 -0
- package/dist/ui/icons.js +71 -0
- package/dist/ui/icons.js.map +1 -0
- package/dist/ui/theme.d.ts +72 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +170 -0
- package/dist/ui/theme.js.map +1 -0
- package/docs/backend-contract.md +392 -0
- package/docs/migration-notes.md +32 -0
- package/package.json +60 -0
- package/src/api/conversation-api.ts +71 -0
- package/src/api/file-api.ts +14 -0
- package/src/api/widget-api.ts +12 -0
- package/src/axios/axios.ts +159 -0
- package/src/core/config.ts +54 -0
- package/src/core/http-client.ts +136 -0
- package/src/core/identity.ts +68 -0
- package/src/core/visitor-params.ts +48 -0
- package/src/hooks/use-conversations.ts +181 -0
- package/src/hooks/use-live-chat.ts +84 -0
- package/src/hooks/use-messages.ts +188 -0
- package/src/hooks/use-send-message.ts +159 -0
- package/src/index.ts +114 -0
- package/src/navigation/LiveChatPanel.tsx +118 -0
- package/src/navigation/panel-router-context.tsx +89 -0
- package/src/navigation/router-types.ts +1 -0
- package/src/provider/LiveChatContext.ts +33 -0
- package/src/provider/LiveChatProvider.tsx +380 -0
- package/src/provider/types.ts +57 -0
- package/src/realtime/ws-client.ts +369 -0
- package/src/types/react-native-svg.d.ts +10 -0
- package/src/ui/components/AssigneeAvatar.tsx +102 -0
- package/src/ui/components/Avatar.tsx +110 -0
- package/src/ui/components/ConversationHeader.tsx +202 -0
- package/src/ui/components/ConversationListScreen.tsx +454 -0
- package/src/ui/components/ConversationScreen.tsx +362 -0
- package/src/ui/components/HomeScreen.tsx +278 -0
- package/src/ui/components/LivechatMessageRenderer.tsx +268 -0
- package/src/ui/components/LogMessage.tsx +88 -0
- package/src/ui/components/MessageBubble.tsx +148 -0
- package/src/ui/components/MessageComposer.tsx +461 -0
- package/src/ui/components/WsStatusStrip.tsx +123 -0
- package/src/ui/icons.tsx +111 -0
- package/src/ui/theme.ts +237 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# Backend Contract
|
|
2
|
+
|
|
3
|
+
This document describes every backend endpoint and WebSocket event consumed by the
|
|
4
|
+
`@turbodev/livechat-react-native` SDK. It is extracted from the web widget
|
|
5
|
+
(`turbodev-livchat-widget`) and applies 1-to-1 to the mobile SDK because both share
|
|
6
|
+
the same backend.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Base URL
|
|
11
|
+
|
|
12
|
+
All REST calls are made to `apiBaseUrl` (supplied by the client at provider setup).
|
|
13
|
+
Example: `https://api.example.com`
|
|
14
|
+
|
|
15
|
+
WebSocket: the SDK converts the scheme automatically — `http` → `ws`, `https` → `wss`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Authentication
|
|
20
|
+
|
|
21
|
+
### Guest mode (no token)
|
|
22
|
+
- A stable random `userId` is generated and persisted in `AsyncStorage` under the key
|
|
23
|
+
`lcw_uid_{widgetId}`.
|
|
24
|
+
- Every request includes `?widgetId=…&userId=…` as query params.
|
|
25
|
+
|
|
26
|
+
### Authenticated mode (token)
|
|
27
|
+
- Pass `userToken` to the provider or call `setToken(token)` at runtime.
|
|
28
|
+
- Every request adds `Authorization: Bearer {userToken}` header.
|
|
29
|
+
- Query params: `?widgetId=…&userToken=…` (and optionally `userId=…` if both are set).
|
|
30
|
+
|
|
31
|
+
### Visitor profile params (optional extra query params)
|
|
32
|
+
- `name`, `email`, `phone` — sent alongside `widgetId`/`userId`/`userToken` when supplied.
|
|
33
|
+
|
|
34
|
+
### 401 handling
|
|
35
|
+
1. SDK calls the `authCallback` prop to get a fresh token.
|
|
36
|
+
2. The original request is retried once with the new token.
|
|
37
|
+
3. If `authCallback` is not set, the error propagates to the hook caller.
|
|
38
|
+
|
|
39
|
+
### 403 handling
|
|
40
|
+
- Request fails immediately; no redirect (unlike the web widget which redirects to `/login`).
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## REST Endpoints
|
|
45
|
+
|
|
46
|
+
### 1. Widget Config
|
|
47
|
+
```
|
|
48
|
+
GET {apiBaseUrl}/api/v1/public/livechat/:widgetId/config
|
|
49
|
+
```
|
|
50
|
+
No auth header required. Public endpoint.
|
|
51
|
+
|
|
52
|
+
**Response** (`response.data`):
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"widgetId": "abc123",
|
|
56
|
+
"widgetName": "Turbodesk Support",
|
|
57
|
+
"workspaceId": "ws_xyz",
|
|
58
|
+
"channelId": "ch_xyz",
|
|
59
|
+
"widgetSettings": {
|
|
60
|
+
"title": "Chat with us",
|
|
61
|
+
"description": "We reply in minutes.",
|
|
62
|
+
"greetings": "Hi! How can we help?",
|
|
63
|
+
"theme": "light",
|
|
64
|
+
"brandColor": "#6366f1",
|
|
65
|
+
"brandLogoUrl": "https://...",
|
|
66
|
+
"afterHoursNote": "We are offline now.",
|
|
67
|
+
"replyTime": "a few minutes",
|
|
68
|
+
"pushNotificationsEnabled": false,
|
|
69
|
+
"requireIdentitySignIn": false,
|
|
70
|
+
"useBrandThemingForChat": false
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### 2. Total Unread Count
|
|
78
|
+
```
|
|
79
|
+
GET {apiBaseUrl}/api/v1/livechat/unread
|
|
80
|
+
Query: widgetId, userId | userToken, [name, email, phone]
|
|
81
|
+
Auth: Bearer token (if token mode)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Response**:
|
|
85
|
+
```json
|
|
86
|
+
{ "data": { "totalUnread": 3 } }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### 3. List Conversations
|
|
92
|
+
```
|
|
93
|
+
GET {apiBaseUrl}/api/v1/livechat/conversations
|
|
94
|
+
Query: widgetId, userId | userToken, page (1-based), limit (default 20)
|
|
95
|
+
Auth: Bearer token (if token mode)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Response**:
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"data": {
|
|
102
|
+
"conversations": [
|
|
103
|
+
{
|
|
104
|
+
"_id": "conv_001",
|
|
105
|
+
"status": "open",
|
|
106
|
+
"latestMessageAt": "2025-01-15T10:30:00Z",
|
|
107
|
+
"latestMessageId": "msg_999",
|
|
108
|
+
"assigneeType": "agent",
|
|
109
|
+
"assigneeId": "agent_42",
|
|
110
|
+
"livechat": { "visitorUnreadCount": 2 },
|
|
111
|
+
"hasUnreadMessages": true,
|
|
112
|
+
"userUnreadCount": 2
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
"pagination": { "hasNextPage": true, "nextPage": 2 }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 4. Get Conversation
|
|
123
|
+
```
|
|
124
|
+
GET {apiBaseUrl}/api/v1/livechat/conversations/:conversationId
|
|
125
|
+
Query: widgetId, userId | userToken
|
|
126
|
+
Auth: Bearer token (if token mode)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Response**:
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"data": {
|
|
133
|
+
"conversation": {
|
|
134
|
+
"_id": "conv_001",
|
|
135
|
+
"status": "open",
|
|
136
|
+
"assigneeType": "agent",
|
|
137
|
+
"assigneeId": "agent_42",
|
|
138
|
+
"userId": "user_abc",
|
|
139
|
+
"messageCount": 12
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 5. Get Messages
|
|
148
|
+
```
|
|
149
|
+
GET {apiBaseUrl}/api/v1/livechat/conversations/:conversationId/messages
|
|
150
|
+
Query: widgetId, userId | userToken, limit (default 30), nextCursor (for pagination)
|
|
151
|
+
Auth: Bearer token (if token mode)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Response**:
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"data": {
|
|
158
|
+
"messages": [
|
|
159
|
+
{
|
|
160
|
+
"_id": "msg_100",
|
|
161
|
+
"senderType": "contact",
|
|
162
|
+
"channelType": "livechat",
|
|
163
|
+
"localMessageId": "lc-1700000000000",
|
|
164
|
+
"createdAt": "2025-01-15T10:28:00Z",
|
|
165
|
+
"livechat": {
|
|
166
|
+
"type": "text",
|
|
167
|
+
"text": { "body": "Hello!" },
|
|
168
|
+
"status": "delivered",
|
|
169
|
+
"timestamp": "2025-01-15T10:28:00Z"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"_id": "msg_101",
|
|
174
|
+
"senderType": "agent",
|
|
175
|
+
"channelType": "livechat",
|
|
176
|
+
"createdAt": "2025-01-15T10:29:00Z",
|
|
177
|
+
"livechat": {
|
|
178
|
+
"type": "text",
|
|
179
|
+
"text": { "body": "Hi! How can I help?" },
|
|
180
|
+
"status": "delivered"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
"pagination": { "hasNextPage": true, "nextCursor": "cursor_xyz" }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### 6. Send Message
|
|
192
|
+
```
|
|
193
|
+
POST {apiBaseUrl}/api/v1/livechat/messages
|
|
194
|
+
Query: widgetId, userId | userToken
|
|
195
|
+
Auth: Bearer token (if token mode)
|
|
196
|
+
Content-Type: application/json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Request body — text message**:
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"localMessageId": "lc-1700000001234",
|
|
203
|
+
"conversationId": "conv_001",
|
|
204
|
+
"message": {
|
|
205
|
+
"type": "text",
|
|
206
|
+
"text": { "body": "Hello from mobile!" }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
If `conversationId` is omitted, the server creates a new conversation and returns its id.
|
|
211
|
+
|
|
212
|
+
**Request body — file/image**:
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"localMessageId": "lc-1700000001235",
|
|
216
|
+
"conversationId": "conv_001",
|
|
217
|
+
"fileId": "file_abc",
|
|
218
|
+
"fileUrl": "https://cdn.example.com/file.jpg",
|
|
219
|
+
"fileName": "photo.jpg",
|
|
220
|
+
"fileExtension": "jpg",
|
|
221
|
+
"message": {
|
|
222
|
+
"type": "image",
|
|
223
|
+
"image": { "link": "https://cdn.example.com/file.jpg" }
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Response**:
|
|
229
|
+
```json
|
|
230
|
+
{ "data": { "conversationId": "conv_001" } }
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### 7. Mark Conversation Read
|
|
236
|
+
```
|
|
237
|
+
PUT {apiBaseUrl}/api/v1/livechat/conversations/:conversationId/read
|
|
238
|
+
Query: widgetId, userId | userToken
|
|
239
|
+
Auth: Bearer token (if token mode)
|
|
240
|
+
Body: {}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Response**: `200 OK` (body ignored)
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### 8. Conversation Count
|
|
248
|
+
```
|
|
249
|
+
GET {apiBaseUrl}/api/v1/livechat/conversations/count
|
|
250
|
+
Query: widgetId, userId | userToken, [extra filters]
|
|
251
|
+
Auth: Bearer token (if token mode)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Response**:
|
|
255
|
+
```json
|
|
256
|
+
{ "data": { "count": 5 } }
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### 9. File Presign (upload)
|
|
262
|
+
```
|
|
263
|
+
POST {apiBaseUrl}/api/v1/livechat/files/presign
|
|
264
|
+
Query/Auth: same as other endpoints
|
|
265
|
+
Content-Type: application/json
|
|
266
|
+
Body: { "fileName": "photo.jpg", "fileType": "image/jpeg", "widgetId": "abc" }
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Response**:
|
|
270
|
+
```json
|
|
271
|
+
{
|
|
272
|
+
"data": {
|
|
273
|
+
"uploadUrl": "https://s3.example.com/presigned-put-url",
|
|
274
|
+
"fileId": "file_abc",
|
|
275
|
+
"fileUrl": "https://cdn.example.com/file.jpg",
|
|
276
|
+
"fields": {}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## WebSocket
|
|
284
|
+
|
|
285
|
+
### Connection URL
|
|
286
|
+
```
|
|
287
|
+
{wsBase}/api/v1/ws/livechat?widgetId=…&userToken=…
|
|
288
|
+
widgetId=…&userId=… (guest mode)
|
|
289
|
+
```
|
|
290
|
+
`wsBase` = `apiBaseUrl` with scheme replaced (`http→ws`, `https→wss`).
|
|
291
|
+
|
|
292
|
+
### Connection lifecycle
|
|
293
|
+
- SDK connects as soon as `widgetId` is available.
|
|
294
|
+
- Exponential backoff on disconnect: 1 s → 2 s → 4 s … max 30 s.
|
|
295
|
+
- Reconnects on: app foreground (`AppState` active), network restore (`NetInfo`).
|
|
296
|
+
- Connecting timeout: 20 s (then force-reconnect).
|
|
297
|
+
- Idle safety reconnect: 30 min without any message.
|
|
298
|
+
|
|
299
|
+
### Message envelope
|
|
300
|
+
All WS messages are JSON with a top-level `type` field:
|
|
301
|
+
```json
|
|
302
|
+
{ "type": "new_livechat_message", "data": { ... } }
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Events
|
|
306
|
+
|
|
307
|
+
#### `new_livechat_message`
|
|
308
|
+
New message in any conversation the visitor is part of.
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"type": "new_livechat_message",
|
|
312
|
+
"data": {
|
|
313
|
+
"conversation": { "_id": "conv_001", "status": "open", "livechat": { "visitorUnreadCount": 1 } },
|
|
314
|
+
"message": {
|
|
315
|
+
"_id": "msg_102",
|
|
316
|
+
"senderType": "agent",
|
|
317
|
+
"channelType": "livechat",
|
|
318
|
+
"localMessageId": null,
|
|
319
|
+
"createdAt": "2025-01-15T10:35:00Z",
|
|
320
|
+
"livechat": { "type": "text", "text": { "body": "Reply from agent" }, "status": "delivered" }
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
SDK action: append message to active conversation list; increment `totalUnread` (if sender is not `"contact"`).
|
|
326
|
+
|
|
327
|
+
#### `new_log_message`
|
|
328
|
+
System/log message (e.g. "Conversation assigned to Agent X").
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"type": "new_log_message",
|
|
332
|
+
"data": {
|
|
333
|
+
"conversation": { "_id": "conv_001" },
|
|
334
|
+
"message": { "_id": "log_01", "channelType": "log", "livechat": { "type": "log", "text": { "body": "Assigned to Agent" } } }
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
SDK action: append to messages list.
|
|
339
|
+
|
|
340
|
+
#### `visitor_conversation_messages_marked_as_read`
|
|
341
|
+
Visitor opened the conversation; server confirms read.
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"type": "visitor_conversation_messages_marked_as_read",
|
|
345
|
+
"data": { "conversationId": "conv_001", "visitorUnreadCount": 0, "messageCount": 3 }
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
SDK action: update `totalUnread` using `visitorUnreadCount` or decrement by `messageCount`.
|
|
349
|
+
|
|
350
|
+
#### `conversation_intervened`
|
|
351
|
+
A bot/agent started handling the conversation.
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"type": "conversation_intervened",
|
|
355
|
+
"data": {
|
|
356
|
+
"conversation": { "_id": "conv_001", "assigneeType": "agent", "assigneeId": "agent_42", "status": "open" }
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
SDK action: patch conversation in list and detail view.
|
|
361
|
+
|
|
362
|
+
#### `conversation_unassigned`
|
|
363
|
+
Conversation unassigned (returned to queue).
|
|
364
|
+
```json
|
|
365
|
+
{ "type": "conversation_unassigned", "data": { "conversation": { "_id": "conv_001", "assigneeType": null, "assigneeId": null } } }
|
|
366
|
+
```
|
|
367
|
+
SDK action: patch conversation.
|
|
368
|
+
|
|
369
|
+
#### `conversation_transferred`
|
|
370
|
+
Conversation transferred to another agent/team.
|
|
371
|
+
```json
|
|
372
|
+
{ "type": "conversation_transferred", "data": { "conversation": { "_id": "conv_001", "assigneeType": "agent", "assigneeId": "agent_99" } } }
|
|
373
|
+
```
|
|
374
|
+
SDK action: patch conversation.
|
|
375
|
+
|
|
376
|
+
#### `conversation_resolved`
|
|
377
|
+
Conversation closed by agent.
|
|
378
|
+
```json
|
|
379
|
+
{ "type": "conversation_resolved", "data": { "conversation": { "_id": "conv_001", "status": "resolved" } } }
|
|
380
|
+
```
|
|
381
|
+
SDK action: patch conversation; disable composer in ConversationScreen.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Response unwrapping
|
|
386
|
+
|
|
387
|
+
Responses may come in multiple shapes depending on backend version:
|
|
388
|
+
- `response.data.data.X` — double-wrapped
|
|
389
|
+
- `response.data.X` — single-wrapped
|
|
390
|
+
- `response.data` (array) — bare array
|
|
391
|
+
|
|
392
|
+
The SDK defensively unwraps all of these.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Migration Notes
|
|
2
|
+
|
|
3
|
+
This file tracks API changes that require action when upgrading.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v0.1.0-alpha → future versions
|
|
8
|
+
|
|
9
|
+
### Planned API changes (not yet breaking)
|
|
10
|
+
- `useSendMessage` may gain optimistic update support
|
|
11
|
+
- `useMessages` may expose a `refetch` method
|
|
12
|
+
- `LiveChatProvider.theme` will likely accept a full or partial `ThemeConfig`
|
|
13
|
+
|
|
14
|
+
### Things that will NOT change (stable API surface)
|
|
15
|
+
- `LiveChatProvider` prop names (widgetId, apiBaseUrl, userToken, authCallback)
|
|
16
|
+
- All 4 hook names and their primary return values
|
|
17
|
+
- WS event names (new_livechat_message, conversation_resolved, etc.)
|
|
18
|
+
- REST endpoint paths (backend contract is shared with the web widget)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Upgrading from web widget (`turbodev-livchat-widget`) to mobile SDK
|
|
23
|
+
|
|
24
|
+
This package is a fresh React Native build. There is no direct upgrade path.
|
|
25
|
+
|
|
26
|
+
| Web widget pattern | Mobile SDK equivalent |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `window.LCWidget.init({ widgetId })` | `<LiveChatProvider widgetId="..." />` |
|
|
29
|
+
| `useLivechatWs("event", cb)` | `wsClient.subscribe("event", cb)` (via `useLiveChat()` → `.wsClient`) |
|
|
30
|
+
| `localStorage` fingerprint | `AsyncStorage` fingerprint (`src/core/identity.ts`) |
|
|
31
|
+
| Tailwind classes | `StyleSheet` + `ThemeConfig` |
|
|
32
|
+
| Panel router / hash history | Use your app navigator (React Navigation recommended) |
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "turbodesk-livechat-react-native",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Headless Live Chat SDK for React Native - REST, WebSocket, hooks, and optional UI kit",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"README.md",
|
|
19
|
+
"docs",
|
|
20
|
+
"CHANGELOG.md"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.build.json",
|
|
25
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
26
|
+
"prepack": "npm run clean && npm run build",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18.0.0",
|
|
31
|
+
"react-native": ">=0.72.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"@react-native-async-storage/async-storage": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"@react-native-community/netinfo": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"react-native-svg": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"react-native-image-picker": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"react-native-document-picker": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"react-native-linear-gradient": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/react": "^18.3.20",
|
|
55
|
+
"@types/react-native": "^0.73.0",
|
|
56
|
+
"react": "^18.3.1",
|
|
57
|
+
"react-native": "~0.76.9",
|
|
58
|
+
"typescript": "^5.6.3"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import getApiClient, { type RequestOptions } from "../axios/axios";
|
|
2
|
+
|
|
3
|
+
const apiClient = getApiClient();
|
|
4
|
+
|
|
5
|
+
export const conversationApi = {
|
|
6
|
+
getConversationCount: async (params?: RequestOptions["params"]) => {
|
|
7
|
+
const response = await apiClient.get<any>(
|
|
8
|
+
`api/v1/livechat/conversations/count`,
|
|
9
|
+
params ? { params } : undefined
|
|
10
|
+
);
|
|
11
|
+
return response.data;
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
getTotalUnread: async (options?: RequestOptions) => {
|
|
15
|
+
const response = await apiClient.get<any>(
|
|
16
|
+
`api/v1/livechat/unread`,
|
|
17
|
+
options
|
|
18
|
+
);
|
|
19
|
+
return response.data;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
getConversations: async (options?: RequestOptions) => {
|
|
23
|
+
const response = await apiClient.get<any>(
|
|
24
|
+
`api/v1/livechat/conversations`,
|
|
25
|
+
options
|
|
26
|
+
);
|
|
27
|
+
return response.data;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getConversation: async (conversationId: string, options?: RequestOptions) => {
|
|
31
|
+
const response = await apiClient.get<any>(
|
|
32
|
+
`api/v1/livechat/conversations/${conversationId}`,
|
|
33
|
+
options
|
|
34
|
+
);
|
|
35
|
+
return response.data;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Visitor message entrypoint (`POST /api/v1/livechat/messages`).
|
|
40
|
+
* If `conversationId` is absent in the body, the server creates a new conversation
|
|
41
|
+
* for the resolved contact.
|
|
42
|
+
*/
|
|
43
|
+
sendVisitorMessage: async (data: any, options?: RequestOptions) => {
|
|
44
|
+
const response = await apiClient.post<any>(
|
|
45
|
+
`api/v1/livechat/messages`,
|
|
46
|
+
data,
|
|
47
|
+
options
|
|
48
|
+
);
|
|
49
|
+
return response.data;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
getMessages: async (conversationId: string, options?: RequestOptions) => {
|
|
53
|
+
const response = await apiClient.get<any>(
|
|
54
|
+
`api/v1/livechat/conversations/${conversationId}/messages`,
|
|
55
|
+
options
|
|
56
|
+
);
|
|
57
|
+
return response.data;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
markMessageAsRead: async (
|
|
61
|
+
conversationId: string,
|
|
62
|
+
options?: RequestOptions
|
|
63
|
+
) => {
|
|
64
|
+
const response = await apiClient.put<any>(
|
|
65
|
+
`api/v1/livechat/conversations/${conversationId}/read`,
|
|
66
|
+
{},
|
|
67
|
+
options
|
|
68
|
+
);
|
|
69
|
+
return response.data;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import getApiClient, { type RequestOptions } from "../axios/axios";
|
|
2
|
+
|
|
3
|
+
const apiClient = getApiClient();
|
|
4
|
+
|
|
5
|
+
export const fileApi = {
|
|
6
|
+
getFileUrl: async (params: any, options?: RequestOptions) => {
|
|
7
|
+
const response = await apiClient.post(
|
|
8
|
+
`/api/v1/livechat/files/presign`,
|
|
9
|
+
params,
|
|
10
|
+
options
|
|
11
|
+
);
|
|
12
|
+
return response.data;
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import getApiClient from "../axios/axios";
|
|
2
|
+
|
|
3
|
+
const apiClient = getApiClient();
|
|
4
|
+
|
|
5
|
+
export const widgetApi = {
|
|
6
|
+
getWidget: async (widgetId: string) => {
|
|
7
|
+
const response = await apiClient.get<any>(
|
|
8
|
+
`api/v1/public/livechat/${widgetId}/config`
|
|
9
|
+
);
|
|
10
|
+
return response.data;
|
|
11
|
+
},
|
|
12
|
+
};
|