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.
Files changed (171) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +91 -0
  3. package/dist/api/conversation-api.d.ts +16 -0
  4. package/dist/api/conversation-api.d.ts.map +1 -0
  5. package/dist/api/conversation-api.js +44 -0
  6. package/dist/api/conversation-api.js.map +1 -0
  7. package/dist/api/file-api.d.ts +5 -0
  8. package/dist/api/file-api.d.ts.map +1 -0
  9. package/dist/api/file-api.js +15 -0
  10. package/dist/api/file-api.js.map +1 -0
  11. package/dist/api/widget-api.d.ts +4 -0
  12. package/dist/api/widget-api.d.ts.map +1 -0
  13. package/dist/api/widget-api.js +15 -0
  14. package/dist/api/widget-api.js.map +1 -0
  15. package/dist/axios/axios.d.ts +32 -0
  16. package/dist/axios/axios.d.ts.map +1 -0
  17. package/dist/axios/axios.js +120 -0
  18. package/dist/axios/axios.js.map +1 -0
  19. package/dist/core/config.d.ts +17 -0
  20. package/dist/core/config.d.ts.map +1 -0
  21. package/dist/core/config.js +42 -0
  22. package/dist/core/config.js.map +1 -0
  23. package/dist/core/http-client.d.ts +33 -0
  24. package/dist/core/http-client.d.ts.map +1 -0
  25. package/dist/core/http-client.js +104 -0
  26. package/dist/core/http-client.js.map +1 -0
  27. package/dist/core/identity.d.ts +7 -0
  28. package/dist/core/identity.d.ts.map +1 -0
  29. package/dist/core/identity.js +62 -0
  30. package/dist/core/identity.js.map +1 -0
  31. package/dist/core/visitor-params.d.ts +15 -0
  32. package/dist/core/visitor-params.d.ts.map +1 -0
  33. package/dist/core/visitor-params.js +45 -0
  34. package/dist/core/visitor-params.js.map +1 -0
  35. package/dist/hooks/use-conversations.d.ts +12 -0
  36. package/dist/hooks/use-conversations.d.ts.map +1 -0
  37. package/dist/hooks/use-conversations.js +177 -0
  38. package/dist/hooks/use-conversations.js.map +1 -0
  39. package/dist/hooks/use-live-chat.d.ts +30 -0
  40. package/dist/hooks/use-live-chat.d.ts.map +1 -0
  41. package/dist/hooks/use-live-chat.js +52 -0
  42. package/dist/hooks/use-live-chat.js.map +1 -0
  43. package/dist/hooks/use-messages.d.ts +11 -0
  44. package/dist/hooks/use-messages.d.ts.map +1 -0
  45. package/dist/hooks/use-messages.js +185 -0
  46. package/dist/hooks/use-messages.js.map +1 -0
  47. package/dist/hooks/use-send-message.d.ts +22 -0
  48. package/dist/hooks/use-send-message.d.ts.map +1 -0
  49. package/dist/hooks/use-send-message.js +125 -0
  50. package/dist/hooks/use-send-message.js.map +1 -0
  51. package/dist/index.d.ts +49 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +97 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/navigation/LiveChatPanel.d.ts +5 -0
  56. package/dist/navigation/LiveChatPanel.d.ts.map +1 -0
  57. package/dist/navigation/LiveChatPanel.js +81 -0
  58. package/dist/navigation/LiveChatPanel.js.map +1 -0
  59. package/dist/navigation/panel-router-context.d.ts +22 -0
  60. package/dist/navigation/panel-router-context.d.ts.map +1 -0
  61. package/dist/navigation/panel-router-context.js +42 -0
  62. package/dist/navigation/panel-router-context.js.map +1 -0
  63. package/dist/navigation/router-types.d.ts +2 -0
  64. package/dist/navigation/router-types.d.ts.map +1 -0
  65. package/dist/navigation/router-types.js +3 -0
  66. package/dist/navigation/router-types.js.map +1 -0
  67. package/dist/provider/LiveChatContext.d.ts +4 -0
  68. package/dist/provider/LiveChatContext.d.ts.map +1 -0
  69. package/dist/provider/LiveChatContext.js +35 -0
  70. package/dist/provider/LiveChatContext.js.map +1 -0
  71. package/dist/provider/LiveChatProvider.d.ts +3 -0
  72. package/dist/provider/LiveChatProvider.d.ts.map +1 -0
  73. package/dist/provider/LiveChatProvider.js +308 -0
  74. package/dist/provider/LiveChatProvider.js.map +1 -0
  75. package/dist/provider/types.d.ts +42 -0
  76. package/dist/provider/types.d.ts.map +1 -0
  77. package/dist/provider/types.js +3 -0
  78. package/dist/provider/types.js.map +1 -0
  79. package/dist/realtime/ws-client.d.ts +51 -0
  80. package/dist/realtime/ws-client.d.ts.map +1 -0
  81. package/dist/realtime/ws-client.js +322 -0
  82. package/dist/realtime/ws-client.js.map +1 -0
  83. package/dist/ui/components/AssigneeAvatar.d.ts +12 -0
  84. package/dist/ui/components/AssigneeAvatar.d.ts.map +1 -0
  85. package/dist/ui/components/AssigneeAvatar.js +58 -0
  86. package/dist/ui/components/AssigneeAvatar.js.map +1 -0
  87. package/dist/ui/components/Avatar.d.ts +10 -0
  88. package/dist/ui/components/Avatar.d.ts.map +1 -0
  89. package/dist/ui/components/Avatar.js +76 -0
  90. package/dist/ui/components/Avatar.js.map +1 -0
  91. package/dist/ui/components/ConversationHeader.d.ts +10 -0
  92. package/dist/ui/components/ConversationHeader.d.ts.map +1 -0
  93. package/dist/ui/components/ConversationHeader.js +90 -0
  94. package/dist/ui/components/ConversationHeader.js.map +1 -0
  95. package/dist/ui/components/ConversationListScreen.d.ts +9 -0
  96. package/dist/ui/components/ConversationListScreen.d.ts.map +1 -0
  97. package/dist/ui/components/ConversationListScreen.js +350 -0
  98. package/dist/ui/components/ConversationListScreen.js.map +1 -0
  99. package/dist/ui/components/ConversationScreen.d.ts +8 -0
  100. package/dist/ui/components/ConversationScreen.d.ts.map +1 -0
  101. package/dist/ui/components/ConversationScreen.js +235 -0
  102. package/dist/ui/components/ConversationScreen.js.map +1 -0
  103. package/dist/ui/components/HomeScreen.d.ts +6 -0
  104. package/dist/ui/components/HomeScreen.d.ts.map +1 -0
  105. package/dist/ui/components/HomeScreen.js +133 -0
  106. package/dist/ui/components/HomeScreen.js.map +1 -0
  107. package/dist/ui/components/LivechatMessageRenderer.d.ts +17 -0
  108. package/dist/ui/components/LivechatMessageRenderer.d.ts.map +1 -0
  109. package/dist/ui/components/LivechatMessageRenderer.js +122 -0
  110. package/dist/ui/components/LivechatMessageRenderer.js.map +1 -0
  111. package/dist/ui/components/LogMessage.d.ts +5 -0
  112. package/dist/ui/components/LogMessage.d.ts.map +1 -0
  113. package/dist/ui/components/LogMessage.js +83 -0
  114. package/dist/ui/components/LogMessage.js.map +1 -0
  115. package/dist/ui/components/MessageBubble.d.ts +15 -0
  116. package/dist/ui/components/MessageBubble.d.ts.map +1 -0
  117. package/dist/ui/components/MessageBubble.js +84 -0
  118. package/dist/ui/components/MessageBubble.js.map +1 -0
  119. package/dist/ui/components/MessageComposer.d.ts +31 -0
  120. package/dist/ui/components/MessageComposer.d.ts.map +1 -0
  121. package/dist/ui/components/MessageComposer.js +295 -0
  122. package/dist/ui/components/MessageComposer.js.map +1 -0
  123. package/dist/ui/components/WsStatusStrip.d.ts +2 -0
  124. package/dist/ui/components/WsStatusStrip.d.ts.map +1 -0
  125. package/dist/ui/components/WsStatusStrip.js +103 -0
  126. package/dist/ui/components/WsStatusStrip.js.map +1 -0
  127. package/dist/ui/icons.d.ts +22 -0
  128. package/dist/ui/icons.d.ts.map +1 -0
  129. package/dist/ui/icons.js +71 -0
  130. package/dist/ui/icons.js.map +1 -0
  131. package/dist/ui/theme.d.ts +72 -0
  132. package/dist/ui/theme.d.ts.map +1 -0
  133. package/dist/ui/theme.js +170 -0
  134. package/dist/ui/theme.js.map +1 -0
  135. package/docs/backend-contract.md +392 -0
  136. package/docs/migration-notes.md +32 -0
  137. package/package.json +60 -0
  138. package/src/api/conversation-api.ts +71 -0
  139. package/src/api/file-api.ts +14 -0
  140. package/src/api/widget-api.ts +12 -0
  141. package/src/axios/axios.ts +159 -0
  142. package/src/core/config.ts +54 -0
  143. package/src/core/http-client.ts +136 -0
  144. package/src/core/identity.ts +68 -0
  145. package/src/core/visitor-params.ts +48 -0
  146. package/src/hooks/use-conversations.ts +181 -0
  147. package/src/hooks/use-live-chat.ts +84 -0
  148. package/src/hooks/use-messages.ts +188 -0
  149. package/src/hooks/use-send-message.ts +159 -0
  150. package/src/index.ts +114 -0
  151. package/src/navigation/LiveChatPanel.tsx +118 -0
  152. package/src/navigation/panel-router-context.tsx +89 -0
  153. package/src/navigation/router-types.ts +1 -0
  154. package/src/provider/LiveChatContext.ts +33 -0
  155. package/src/provider/LiveChatProvider.tsx +380 -0
  156. package/src/provider/types.ts +57 -0
  157. package/src/realtime/ws-client.ts +369 -0
  158. package/src/types/react-native-svg.d.ts +10 -0
  159. package/src/ui/components/AssigneeAvatar.tsx +102 -0
  160. package/src/ui/components/Avatar.tsx +110 -0
  161. package/src/ui/components/ConversationHeader.tsx +202 -0
  162. package/src/ui/components/ConversationListScreen.tsx +454 -0
  163. package/src/ui/components/ConversationScreen.tsx +362 -0
  164. package/src/ui/components/HomeScreen.tsx +278 -0
  165. package/src/ui/components/LivechatMessageRenderer.tsx +268 -0
  166. package/src/ui/components/LogMessage.tsx +88 -0
  167. package/src/ui/components/MessageBubble.tsx +148 -0
  168. package/src/ui/components/MessageComposer.tsx +461 -0
  169. package/src/ui/components/WsStatusStrip.tsx +123 -0
  170. package/src/ui/icons.tsx +111 -0
  171. 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
+ };