@teincfood/chat-client 0.1.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/README.md +437 -0
- package/dist/api/auth.d.ts +13 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +27 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/contacts.d.ts +6 -0
- package/dist/api/contacts.d.ts.map +1 -0
- package/dist/api/contacts.js +21 -0
- package/dist/api/contacts.js.map +1 -0
- package/dist/api/conversations.d.ts +21 -0
- package/dist/api/conversations.d.ts.map +1 -0
- package/dist/api/conversations.js +24 -0
- package/dist/api/conversations.js.map +1 -0
- package/dist/api/messages.d.ts +11 -0
- package/dist/api/messages.d.ts.map +1 -0
- package/dist/api/messages.js +21 -0
- package/dist/api/messages.js.map +1 -0
- package/dist/api/notifications.d.ts +15 -0
- package/dist/api/notifications.d.ts.map +1 -0
- package/dist/api/notifications.js +12 -0
- package/dist/api/notifications.js.map +1 -0
- package/dist/api/upload.d.ts +6 -0
- package/dist/api/upload.d.ts.map +1 -0
- package/dist/api/upload.js +15 -0
- package/dist/api/upload.js.map +1 -0
- package/dist/client.d.ts +98 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +277 -0
- package/dist/client.js.map +1 -0
- package/dist/events.d.ts +9 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +28 -0
- package/dist/events.js.map +1 -0
- package/dist/formatters.d.ts +4 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +32 -0
- package/dist/formatters.js.map +1 -0
- package/dist/http.d.ts +25 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +90 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.test.d.ts +10 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +184 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/optimistic.d.ts +26 -0
- package/dist/optimistic.d.ts.map +1 -0
- package/dist/optimistic.js +85 -0
- package/dist/optimistic.js.map +1 -0
- package/dist/react.d.ts +26 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +208 -0
- package/dist/react.js.map +1 -0
- package/dist/retry.d.ts +7 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +27 -0
- package/dist/retry.js.map +1 -0
- package/dist/socket.d.ts +83 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +615 -0
- package/dist/socket.js.map +1 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +185 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/unit.test.d.ts +8 -0
- package/dist/unit.test.d.ts.map +1 -0
- package/dist/unit.test.js +425 -0
- package/dist/unit.test.js.map +1 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
# teincfood-chat-client
|
|
2
|
+
|
|
3
|
+
Headless, event-driven chat client for TeincFood. Connects to the TeincFood Phoenix backend via raw WebSocket (Phoenix v2 protocol) for real-time messaging and REST for initial data load.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Event-driven** — all data flows through WebSocket events. No polling.
|
|
8
|
+
- **Optimistic UI** — messages appear instantly, reconciled when the server confirms.
|
|
9
|
+
- **Auto-mark-read** — joining a conversation automatically marks messages as read (like WhatsApp).
|
|
10
|
+
- **Background push handling** — `handlePushPayload()` processes push notification events when the app is in background (where WS is killed).
|
|
11
|
+
- **Pluggable storage** — bring your own adapter (AsyncStorage, MMKV, etc.).
|
|
12
|
+
- **React hooks** — optional `useChat`, `useConversations`, `useContacts`.
|
|
13
|
+
- **Typed events** — full TypeScript types for every event payload.
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
┌──────────────┐ WebSocket (Phoenix v2) ┌─────────────┐
|
|
19
|
+
│ ChatClient │ ────────────────────────────── │ Backend │
|
|
20
|
+
│ │ events:{userId} │ │
|
|
21
|
+
│ ┌────────┐ │ ─ conversation:created │ Phoenix │
|
|
22
|
+
│ │ Store │◄──── ─ conversation:updated │ Channels │
|
|
23
|
+
│ ├────────┤ │ ─ new_message │ │
|
|
24
|
+
│ │ Emitter│ │ ─ messages_read │ │
|
|
25
|
+
│ ├────────┤ │ ─ unread_count │ │
|
|
26
|
+
│ │ Socket │─── │ │
|
|
27
|
+
│ ├────────┤ │ chat:{conversationId} │ │
|
|
28
|
+
│ │Optimist│ │ ─ new_message (send/recv) │ │
|
|
29
|
+
│ └────────┘ │ ─ mark_read │ │
|
|
30
|
+
│ │ ─ typing │ │
|
|
31
|
+
│ │ ─ presence_state/diff │ │
|
|
32
|
+
└──────────────┘ └─────────────┘
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Two channel types:
|
|
36
|
+
|
|
37
|
+
- **`events:{userId}`** — user-level channel. Receives `conversation:created`, `conversation:updated`, `new_message`, `messages_read`, `unread_count`. Auto-joined on `start()`. Uses `last_sequence` to avoid replaying events seen via push notifications.
|
|
38
|
+
- **`chat:{conversationId}`** — per-conversation channel. Used for sending messages, typing indicators, presence, and auto-mark-read on join.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install teincfood-chat-client
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { ChatClient } from "teincfood-chat-client"
|
|
50
|
+
import AsyncStorage from "@react-native-async-storage/async-storage"
|
|
51
|
+
|
|
52
|
+
const storage: ChatStorage = {
|
|
53
|
+
getItem: (key) => AsyncStorage.getItem(key).then((v) => v ? JSON.parse(v) : null),
|
|
54
|
+
setItem: (key, value) => AsyncStorage.setItem(key, JSON.stringify(value)),
|
|
55
|
+
removeItem: (key) => AsyncStorage.removeItem(key),
|
|
56
|
+
clear: () => AsyncStorage.clear(),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const client = new ChatClient({
|
|
60
|
+
baseUrl: "https://api.teincfood.com/api/v1",
|
|
61
|
+
wsUrl: "wss://api.teincfood.com/ws",
|
|
62
|
+
storage,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// Set current user and connect
|
|
66
|
+
client.setCurrentUserId("user-uuid")
|
|
67
|
+
await client.start({
|
|
68
|
+
access_token: "jwt-access-token",
|
|
69
|
+
refresh_token: "jwt-refresh-token",
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Listen for events
|
|
73
|
+
client.on("message:received", (msg) => {
|
|
74
|
+
console.log("New message:", msg.content)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
client.on("conversation:created", ({ conversation }) => {
|
|
78
|
+
console.log("New conversation:", conversation.id)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Join a conversation and send a message
|
|
82
|
+
await client.joinConversation("conv-uuid")
|
|
83
|
+
client.sendMessage("conv-uuid", "Hello, world!")
|
|
84
|
+
|
|
85
|
+
// Receipt: wait for the message:sent confirmation
|
|
86
|
+
client.on("message:sent", (msg) => {
|
|
87
|
+
console.log("Message confirmed by server:", msg.id)
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
| Option | Type | Required | Description |
|
|
94
|
+
|--------|------|----------|-------------|
|
|
95
|
+
| `baseUrl` | `string` | yes | REST API base URL (e.g. `https://api.example.com/api/v1`) |
|
|
96
|
+
| `wsUrl` | `string` | yes | WebSocket endpoint (e.g. `wss://api.example.com/ws`) |
|
|
97
|
+
| `storage` | `ChatStorage` | yes | Persistent storage adapter |
|
|
98
|
+
| `onTokenExpired` | `() => Promise<AuthTokens \| null>` | no | Called on 401. Return new tokens or `null` to trigger `auth:expired`. |
|
|
99
|
+
|
|
100
|
+
## API Reference
|
|
101
|
+
|
|
102
|
+
### Lifecycle
|
|
103
|
+
|
|
104
|
+
#### `start(tokens: AuthTokens): Promise<void>`
|
|
105
|
+
|
|
106
|
+
Hydrate cached data from storage, connect the WebSocket, and auto-join the `events:{userId}` channel.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
await client.start({
|
|
110
|
+
access_token: "...",
|
|
111
|
+
refresh_token: "...",
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### `stop(): void`
|
|
116
|
+
|
|
117
|
+
Disconnect WebSocket, leave events channel, clear optimistic messages and all event listeners.
|
|
118
|
+
|
|
119
|
+
#### `clearCache(): Promise<void>`
|
|
120
|
+
|
|
121
|
+
Clear all local data from the store and storage adapter.
|
|
122
|
+
|
|
123
|
+
### Token & User Management
|
|
124
|
+
|
|
125
|
+
| Method | Returns | Description |
|
|
126
|
+
|--------|---------|-------------|
|
|
127
|
+
| `getTokens()` | `AuthTokens \| null` | Current tokens |
|
|
128
|
+
| `setTokens(tokens)` | `void` | Update tokens |
|
|
129
|
+
| `getCurrentUserId()` | `string \| null` | Current user ID |
|
|
130
|
+
| `setCurrentUserId(userId)` | `void` | Set user ID (auto-joins events channel if WS connected) |
|
|
131
|
+
|
|
132
|
+
### Real-time Messaging
|
|
133
|
+
|
|
134
|
+
#### `sendMessage(conversationId: string, body: string, metadata?: Record<string, unknown> | null): TempMessage`
|
|
135
|
+
|
|
136
|
+
Send a text message. Creates an optimistic message immediately and pushes to the WebSocket. The `message:sent` event fires when the server confirms.
|
|
137
|
+
|
|
138
|
+
- Must be joined to the conversation channel first.
|
|
139
|
+
- Returns a `TempMessage` (has `_clientId` for reconciliation tracking).
|
|
140
|
+
- On failure, `message:failed` event fires and `temp.status` becomes `"failed"`.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
client.sendMessage("conv-1", "Hello!", { priority: "high" })
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### `joinConversation(conversationId: string): Promise<void>`
|
|
147
|
+
|
|
148
|
+
Join a conversation channel for live updates. Automatically pushes `mark_read` on join (like WhatsApp), which triggers `messages_read` events for other participants.
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
await client.joinConversation("conv-1")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### `leaveConversation(conversationId: string): void`
|
|
155
|
+
|
|
156
|
+
Leave a conversation channel.
|
|
157
|
+
|
|
158
|
+
#### `markRead(conversationId: string): void`
|
|
159
|
+
|
|
160
|
+
Manually push `mark_read` to a conversation.
|
|
161
|
+
|
|
162
|
+
#### `sendTyping(conversationId: string): void`
|
|
163
|
+
|
|
164
|
+
Push a typing indicator to a conversation.
|
|
165
|
+
|
|
166
|
+
#### `fetchMessages(conversationId: string, limit?: number): void`
|
|
167
|
+
|
|
168
|
+
Request message history via WebSocket. Emits `messages:loaded` when the server responds.
|
|
169
|
+
|
|
170
|
+
### REST Helpers
|
|
171
|
+
|
|
172
|
+
These make one-time HTTP requests and cache the results in the store. Primarily used for initial data load or fallback.
|
|
173
|
+
|
|
174
|
+
| Method | Returns | Description |
|
|
175
|
+
|--------|---------|-------------|
|
|
176
|
+
| `loadInbox()` | `Promise<Conversation[]>` | Fetch inbox from REST, cache in store |
|
|
177
|
+
| `loadContacts(context?, businessId?, q?)` | `Promise<Contact[]>` | Fetch contacts from REST, cache in store |
|
|
178
|
+
| `loadMessages(convId, limit?, offset?)` | `Promise<{ data: Message[]; meta: MessageListMeta }>` | Fetch message page from REST, cache in store |
|
|
179
|
+
|
|
180
|
+
### Background Push Notification Handling
|
|
181
|
+
|
|
182
|
+
#### `handlePushPayload(payload: Record<string, unknown>): PushEventResult`
|
|
183
|
+
|
|
184
|
+
Process a remote push notification payload outside the WebSocket connection.
|
|
185
|
+
|
|
186
|
+
On mobile platforms, the WebSocket is killed when the app goes to background. Push notifications carry the event data directly. This method routes the payload through the same store + event logic as the live events channel.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// In your React Native push notification handler:
|
|
190
|
+
function onPushNotification(notification: { data: Record<string, unknown> }) {
|
|
191
|
+
const result = client.handlePushPayload(notification.data)
|
|
192
|
+
if (result.handled) {
|
|
193
|
+
console.log(`Processed ${result.event}`)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**`PushEventResult`:**
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
interface PushEventResult {
|
|
202
|
+
handled: boolean
|
|
203
|
+
event?: ChatEventName
|
|
204
|
+
data?: Record<string, unknown>
|
|
205
|
+
sequence?: number
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
When the WS reconnects, `joinEventsChannel` sends `{ last_sequence: N }` to avoid replaying events already seen via push notifications.
|
|
210
|
+
|
|
211
|
+
### Sub-APIs
|
|
212
|
+
|
|
213
|
+
Each sub-API is a namespace of REST methods accessible via `client.*`:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
client.auth // login, register, refresh, otp, oidc
|
|
217
|
+
client.conversations // getInbox, getConversation, startChat, close, listSupportConversations
|
|
218
|
+
client.messages // list, sendText, sendMedia
|
|
219
|
+
client.contacts // getContacts
|
|
220
|
+
client.notifications // list, getUnreadCount
|
|
221
|
+
client.upload // getUploadUrl
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Event Subscriptions
|
|
225
|
+
|
|
226
|
+
#### `on<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): () => void`
|
|
227
|
+
|
|
228
|
+
Subscribe to an event. Returns an unsubscribe function.
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const unsub = client.on("message:received", (msg) => {
|
|
232
|
+
console.log(msg.content)
|
|
233
|
+
})
|
|
234
|
+
// Later:
|
|
235
|
+
unsub()
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### `off<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): void`
|
|
239
|
+
|
|
240
|
+
Remove a specific listener.
|
|
241
|
+
|
|
242
|
+
### Store
|
|
243
|
+
|
|
244
|
+
#### `store: ChatStore`
|
|
245
|
+
|
|
246
|
+
The client's in-memory store, also persisted to the storage adapter.
|
|
247
|
+
|
|
248
|
+
| Method | Returns | Description |
|
|
249
|
+
|--------|---------|-------------|
|
|
250
|
+
| `getMessages(conversationId)` | `Message[]` | Messages for a conversation |
|
|
251
|
+
| `getConversations()` | `Conversation[]` | All cached conversations, sorted by updated_at desc |
|
|
252
|
+
| `getConversation(id)` | `Conversation \| undefined` | Single conversation |
|
|
253
|
+
| `getContacts()` | `Contact[]` | Cached contacts |
|
|
254
|
+
|
|
255
|
+
## Event Reference
|
|
256
|
+
|
|
257
|
+
All 14 events with their payload types:
|
|
258
|
+
|
|
259
|
+
| Event | Payload | Description |
|
|
260
|
+
|-------|---------|-------------|
|
|
261
|
+
| `message:sending` | `TempMessage` | Optimistic message created |
|
|
262
|
+
| `message:sent` | `Message` | Message confirmed by server (via reconciliation) |
|
|
263
|
+
| `message:received` | `Message` | New message from another user |
|
|
264
|
+
| `message:failed` | `TempMessage` | Message send failed |
|
|
265
|
+
| `messages:loaded` | `{ conversationId, messages }` | Message history from `fetchMessages` |
|
|
266
|
+
| `conversation:created` | `{ conversation }` | New conversation from events channel |
|
|
267
|
+
| `conversation:updated` | `{ conversation }` | Conversation updated |
|
|
268
|
+
| `unread_count` | `{ conversationId, unread_count }` | Unread count changed |
|
|
269
|
+
| `typing` | `{ conversationId, userId }` | User is typing |
|
|
270
|
+
| `messages_read` | `{ conversationId, userId, readAt }` | Messages read receipt |
|
|
271
|
+
| `messages_delivered` | `{ conversationId, userId }` | Messages delivered receipt |
|
|
272
|
+
| `presence` | `{ conversationId, users }` | Presence state change |
|
|
273
|
+
| `connection:status` | `"connected" \| "disconnected" \| "reconnecting"` | WS connection state |
|
|
274
|
+
| `auth:expired` | `void` | Token expired (onTokenExpired returned null) |
|
|
275
|
+
| `error` | `Error` | Internal error |
|
|
276
|
+
|
|
277
|
+
## Storage Adapters
|
|
278
|
+
|
|
279
|
+
Implement the `ChatStorage` interface for your platform:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
interface ChatStorage {
|
|
283
|
+
getItem<T = unknown>(key: string): Promise<T | null>
|
|
284
|
+
setItem<T = unknown>(key: string, value: T): Promise<void>
|
|
285
|
+
removeItem(key: string): Promise<void>
|
|
286
|
+
clear(): Promise<void>
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**React Native (AsyncStorage):**
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import AsyncStorage from "@react-native-async-storage/async-storage"
|
|
294
|
+
|
|
295
|
+
const storage: ChatStorage = {
|
|
296
|
+
getItem: (key) => AsyncStorage.getItem(key).then((v) => v ? JSON.parse(v) : null),
|
|
297
|
+
setItem: (key, value) => AsyncStorage.setItem(key, JSON.stringify(value)),
|
|
298
|
+
removeItem: (key) => AsyncStorage.removeItem(key),
|
|
299
|
+
clear: () => AsyncStorage.clear(),
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**React Native (MMKV):**
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
import { MMKV } from "react-native-mmkv"
|
|
307
|
+
|
|
308
|
+
const mmkv = new MMKV()
|
|
309
|
+
|
|
310
|
+
const storage: ChatStorage = {
|
|
311
|
+
getItem: async (key) => {
|
|
312
|
+
const v = mmkv.getString(key)
|
|
313
|
+
return v ? JSON.parse(v) : null
|
|
314
|
+
},
|
|
315
|
+
setItem: async (key, value) => mmkv.set(key, JSON.stringify(value)),
|
|
316
|
+
removeItem: async (key) => mmkv.delete(key),
|
|
317
|
+
clear: async () => mmkv.clearAll(),
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## React Hooks
|
|
322
|
+
|
|
323
|
+
Optional React hooks for component-level state:
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { useChat, useConversations, useContacts } from "teincfood-chat-client"
|
|
327
|
+
|
|
328
|
+
function ChatScreen() {
|
|
329
|
+
const { messages, send } = useChat("conv-1")
|
|
330
|
+
const { conversations } = useConversations()
|
|
331
|
+
const { contacts } = useContacts()
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<FlatList
|
|
335
|
+
data={messages}
|
|
336
|
+
renderItem={({ item }) => <Text>{item.content}</Text>}
|
|
337
|
+
/>
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Hooks use `useSyncExternalStore` under the hood — no `useEffect` polling, purely event-driven.
|
|
343
|
+
|
|
344
|
+
## Background / Foreground Pattern
|
|
345
|
+
|
|
346
|
+
When the app goes to background on iOS/Android, the WebSocket is killed. Push notifications deliver event data directly:
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
App in foreground → WS connected → events arrive in real-time
|
|
350
|
+
App in background → WS killed → push notification arrives
|
|
351
|
+
App tapped / opened → WS reconnects → events channel joins with last_sequence
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// Push notification handler (runs in background)
|
|
356
|
+
function onPushNotification(notification) {
|
|
357
|
+
const result = client.handlePushPayload(notification.data)
|
|
358
|
+
if (result.handled && result.event === "message:received") {
|
|
359
|
+
// Show a local notification
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// App foreground handler
|
|
364
|
+
function onAppForeground() {
|
|
365
|
+
// WS reconnects automatically via start()
|
|
366
|
+
// Events channel uses stored last_sequence to skip duplicates
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Error Handling
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
// HTTP errors (REST calls)
|
|
374
|
+
try {
|
|
375
|
+
await client.loadInbox()
|
|
376
|
+
} catch (err) {
|
|
377
|
+
if (err instanceof HttpError) {
|
|
378
|
+
console.error(err.status, err.message) // e.g. 401, 404, 500
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Auth expiry
|
|
383
|
+
client.on("auth:expired", () => {
|
|
384
|
+
// Redirect to login
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// WS connection changes
|
|
388
|
+
client.on("connection:status", (status) => {
|
|
389
|
+
if (status === "disconnected") {
|
|
390
|
+
// Show offline indicator
|
|
391
|
+
}
|
|
392
|
+
})
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## TypeScript Types
|
|
396
|
+
|
|
397
|
+
All types are exported from the package:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
import type {
|
|
401
|
+
Message, TempMessage,
|
|
402
|
+
Conversation, Contact,
|
|
403
|
+
AuthTokens, ChatStorage,
|
|
404
|
+
ChatEventMap, ChatEventName,
|
|
405
|
+
ChatClientOptions,
|
|
406
|
+
PushEventResult,
|
|
407
|
+
MessagesReadPayload,
|
|
408
|
+
PresencePayload,
|
|
409
|
+
ConversationCreatedPayload,
|
|
410
|
+
ConversationUpdatedPayload,
|
|
411
|
+
} from "teincfood-chat-client"
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Utils
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
import { toMessage, toConversation, retry, HttpError } from "teincfood-chat-client"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
- `toMessage(raw)` — parse a raw payload into a typed `Message`
|
|
421
|
+
- `toConversation(raw)` — parse a raw payload into a typed `Conversation`
|
|
422
|
+
- `retry(fn, options?)` — retry an async function with exponential backoff
|
|
423
|
+
- `HttpError` — HTTP error class with `status`, `statusText`, `detail`
|
|
424
|
+
|
|
425
|
+
## Development
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npm run build # TypeScript compile
|
|
429
|
+
npx vitest run # Run all tests
|
|
430
|
+
npx vitest run --reporter=verbose # Verbose output
|
|
431
|
+
npm test # Alias for vitest
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Test structure
|
|
435
|
+
|
|
436
|
+
- `src/unit.test.ts` — 32 unit tests: store, socket events, push payload, optimistic engine, emitter
|
|
437
|
+
- `src/integration.test.ts` — 6 integration tests against localhost:4000: presence, send/receive, auto-mark-read, auth expiry, error handling
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { AuthTokens, LoginParams, RegisterParams, OtpSendParams, OtpVerifyParams, OidcParams } from "../types";
|
|
3
|
+
export declare function createAuthApi(http: HttpClient): {
|
|
4
|
+
login: (params: LoginParams) => Promise<AuthTokens>;
|
|
5
|
+
register: (params: RegisterParams) => Promise<AuthTokens>;
|
|
6
|
+
refresh: (refreshToken: string) => Promise<AuthTokens>;
|
|
7
|
+
sendOtp: (params: OtpSendParams) => Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
verifyOtp: (params: OtpVerifyParams) => Promise<AuthTokens>;
|
|
11
|
+
oidc: (params: OidcParams) => Promise<AuthTokens>;
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACX,MAAM,UAAU,CAAA;AAMjB,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU;oBAE1B,WAAW;uBAGR,cAAc;4BAKT,MAAM;sBAOZ,aAAa;iBACR,OAAO;;wBAEV,eAAe;mBAKpB,UAAU;EAK5B"}
|
package/dist/api/auth.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAuthApi = createAuthApi;
|
|
4
|
+
function authResponse(res) {
|
|
5
|
+
return res.data;
|
|
6
|
+
}
|
|
7
|
+
function createAuthApi(http) {
|
|
8
|
+
return {
|
|
9
|
+
login: (params) => http.post("/auth/login", params).then(authResponse),
|
|
10
|
+
register: (params) => http
|
|
11
|
+
.post("/auth/register", params)
|
|
12
|
+
.then(authResponse),
|
|
13
|
+
refresh: (refreshToken) => http
|
|
14
|
+
.post("/auth/refresh", {
|
|
15
|
+
refresh_token: refreshToken,
|
|
16
|
+
})
|
|
17
|
+
.then(authResponse),
|
|
18
|
+
sendOtp: (params) => http.post("/auth/otp/send", params),
|
|
19
|
+
verifyOtp: (params) => http
|
|
20
|
+
.post("/auth/otp/verify", params)
|
|
21
|
+
.then(authResponse),
|
|
22
|
+
oidc: (params) => http
|
|
23
|
+
.post("/auth/oidc", params)
|
|
24
|
+
.then(authResponse),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":";;AAcA,sCA8BC;AAlCD,SAAS,YAAY,CAAC,GAAyB;IAC7C,OAAO,GAAG,CAAC,IAAI,CAAA;AACjB,CAAC;AAED,SAAgB,aAAa,CAAC,IAAgB;IAC5C,OAAO;QACL,KAAK,EAAE,CAAC,MAAmB,EAAE,EAAE,CAC7B,IAAI,CAAC,IAAI,CAAuB,aAAa,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;QAE3E,QAAQ,EAAE,CAAC,MAAsB,EAAE,EAAE,CACnC,IAAI;aACD,IAAI,CAAuB,gBAAgB,EAAE,MAAM,CAAC;aACpD,IAAI,CAAC,YAAY,CAAC;QAEvB,OAAO,EAAE,CAAC,YAAoB,EAAE,EAAE,CAChC,IAAI;aACD,IAAI,CAAuB,eAAe,EAAE;YAC3C,aAAa,EAAE,YAAY;SAC5B,CAAC;aACD,IAAI,CAAC,YAAY,CAAC;QAEvB,OAAO,EAAE,CAAC,MAAqB,EAAE,EAAE,CACjC,IAAI,CAAC,IAAI,CAAuB,gBAAgB,EAAE,MAAM,CAAC;QAE3D,SAAS,EAAE,CAAC,MAAuB,EAAE,EAAE,CACrC,IAAI;aACD,IAAI,CAAuB,kBAAkB,EAAE,MAAM,CAAC;aACtD,IAAI,CAAC,YAAY,CAAC;QAEvB,IAAI,EAAE,CAAC,MAAkB,EAAE,EAAE,CAC3B,IAAI;aACD,IAAI,CAAuB,YAAY,EAAE,MAAM,CAAC;aAChD,IAAI,CAAC,YAAY,CAAC;KACxB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { Contact, ContactContext } from "../types";
|
|
3
|
+
export declare function createContactsApi(http: HttpClient): {
|
|
4
|
+
getContacts: (context?: ContactContext, businessId?: string, q?: string) => Promise<Contact[]>;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=contacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["../../src/api/contacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEvD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU;4BAGlC,cAAc,eACX,MAAM,MACf,MAAM;EAYf"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createContactsApi = createContactsApi;
|
|
4
|
+
function createContactsApi(http) {
|
|
5
|
+
return {
|
|
6
|
+
getContacts: (context, businessId, q) => {
|
|
7
|
+
const params = new URLSearchParams();
|
|
8
|
+
if (context)
|
|
9
|
+
params.set("context", context);
|
|
10
|
+
if (businessId)
|
|
11
|
+
params.set("business_id", businessId);
|
|
12
|
+
if (q)
|
|
13
|
+
params.set("q", q);
|
|
14
|
+
const qs = params.toString();
|
|
15
|
+
return http
|
|
16
|
+
.get(`/contacts${qs ? `?${qs}` : ""}`)
|
|
17
|
+
.then((r) => r.data);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=contacts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contacts.js","sourceRoot":"","sources":["../../src/api/contacts.ts"],"names":[],"mappings":";;AAGA,8CAiBC;AAjBD,SAAgB,iBAAiB,CAAC,IAAgB;IAChD,OAAO;QACL,WAAW,EAAE,CACX,OAAwB,EACxB,UAAmB,EACnB,CAAU,EACV,EAAE;YACF,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;YACpC,IAAI,OAAO;gBAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAC3C,IAAI,UAAU;gBAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;YACrD,IAAI,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACzB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;YAC5B,OAAO,IAAI;iBACR,GAAG,CAAsB,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;iBAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { Conversation, PaginationMeta } from "../types";
|
|
3
|
+
export interface StartChatParams {
|
|
4
|
+
contact_type?: "user" | "business" | "support";
|
|
5
|
+
contact_id?: string | null;
|
|
6
|
+
order_id?: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function createConversationsApi(http: HttpClient): {
|
|
9
|
+
getInbox: () => Promise<Conversation[]>;
|
|
10
|
+
getConversation: (id: string) => Promise<Conversation>;
|
|
11
|
+
startChat: (params: StartChatParams) => Promise<Conversation>;
|
|
12
|
+
startSupportChat: () => Promise<Conversation>;
|
|
13
|
+
close: (id: string) => Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
listSupportConversations: (page?: number, pageSize?: number) => Promise<{
|
|
17
|
+
data: Conversation[];
|
|
18
|
+
pagination: PaginationMeta;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=conversations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/api/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IAC9C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU;;0BAO7B,MAAM;wBAKR,eAAe;;gBAUvB,MAAM;iBAEY,OAAO;;;cAK3B,YAAY,EAAE;oBACR,cAAc;;EAGjC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConversationsApi = createConversationsApi;
|
|
4
|
+
function createConversationsApi(http) {
|
|
5
|
+
return {
|
|
6
|
+
getInbox: () => http
|
|
7
|
+
.get("/inbox")
|
|
8
|
+
.then((r) => r.data),
|
|
9
|
+
getConversation: (id) => http
|
|
10
|
+
.get(`/conversations/${id}`)
|
|
11
|
+
.then((r) => r.data),
|
|
12
|
+
startChat: (params) => http
|
|
13
|
+
.post("/chats", params)
|
|
14
|
+
.then((r) => r.data),
|
|
15
|
+
startSupportChat: () => http
|
|
16
|
+
.post("/chats/support")
|
|
17
|
+
.then((r) => r.data),
|
|
18
|
+
close: (id) => http
|
|
19
|
+
.patch(`/conversations/${id}/close`)
|
|
20
|
+
.then((r) => r.data),
|
|
21
|
+
listSupportConversations: (page = 1, pageSize = 20) => http.get(`/chats?page=${page}&page_size=${pageSize}`),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/api/conversations.ts"],"names":[],"mappings":";;AASA,wDAiCC;AAjCD,SAAgB,sBAAsB,CAAC,IAAgB;IACrD,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CACb,IAAI;aACD,GAAG,CAA2B,QAAQ,CAAC;aACvC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,eAAe,EAAE,CAAC,EAAU,EAAE,EAAE,CAC9B,IAAI;aACD,GAAG,CAAyB,kBAAkB,EAAE,EAAE,CAAC;aACnD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,SAAS,EAAE,CAAC,MAAuB,EAAE,EAAE,CACrC,IAAI;aACD,IAAI,CAAyB,QAAQ,EAAE,MAAM,CAAC;aAC9C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,gBAAgB,EAAE,GAAG,EAAE,CACrB,IAAI;aACD,IAAI,CAAyB,gBAAgB,CAAC;aAC9C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CACpB,IAAI;aACD,KAAK,CAAiC,kBAAkB,EAAE,QAAQ,CAAC;aACnE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,wBAAwB,EAAE,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAGL,eAAe,IAAI,cAAc,QAAQ,EAAE,CAAC;KAClD,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { Message, MessageListMeta, MessageType } from "../types";
|
|
3
|
+
export declare function createMessagesApi(http: HttpClient): {
|
|
4
|
+
list: (conversationId: string, limit?: number, offset?: number) => Promise<{
|
|
5
|
+
data: Message[];
|
|
6
|
+
meta: MessageListMeta;
|
|
7
|
+
}>;
|
|
8
|
+
sendText: (conversationId: string, body: string) => Promise<Message>;
|
|
9
|
+
sendMedia: (conversationId: string, key: string, messageType: MessageType, caption?: string) => Promise<Message>;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAErE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU;2BAEvB,MAAM;cAGjB,OAAO,EAAE;cACT,eAAe;;+BAMA,MAAM,QAAQ,MAAM;gCAS7B,MAAM,OACjB,MAAM,eACE,WAAW,YACd,MAAM;EAarB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMessagesApi = createMessagesApi;
|
|
4
|
+
function createMessagesApi(http) {
|
|
5
|
+
return {
|
|
6
|
+
list: (conversationId, limit = 50, offset = 0) => http
|
|
7
|
+
.get(`/conversations/${conversationId}/messages?limit=${limit}&offset=${offset}`)
|
|
8
|
+
.then((r) => r),
|
|
9
|
+
sendText: (conversationId, body) => http
|
|
10
|
+
.post(`/conversations/${conversationId}/messages`, { body })
|
|
11
|
+
.then((r) => r.data),
|
|
12
|
+
sendMedia: (conversationId, key, messageType, caption) => http
|
|
13
|
+
.post(`/conversations/${conversationId}/messages/media`, {
|
|
14
|
+
key,
|
|
15
|
+
message_type: messageType,
|
|
16
|
+
caption,
|
|
17
|
+
})
|
|
18
|
+
.then((r) => r.data),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":";;AAGA,8CAqCC;AArCD,SAAgB,iBAAiB,CAAC,IAAgB;IAChD,OAAO;QACL,IAAI,EAAE,CAAC,cAAsB,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CACvD,IAAI;aACD,GAAG,CAIF,kBAAkB,cAAc,mBAAmB,KAAK,WAAW,MAAM,EAAE,CAC5E;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAEnB,QAAQ,EAAE,CAAC,cAAsB,EAAE,IAAY,EAAE,EAAE,CACjD,IAAI;aACD,IAAI,CACH,kBAAkB,cAAc,WAAW,EAC3C,EAAE,IAAI,EAAE,CACT;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,SAAS,EAAE,CACT,cAAsB,EACtB,GAAW,EACX,WAAwB,EACxB,OAAgB,EAChB,EAAE,CACF,IAAI;aACD,IAAI,CACH,kBAAkB,cAAc,iBAAiB,EACjD;YACE,GAAG;YACH,YAAY,EAAE,WAAW;YACzB,OAAO;SACR,CACF;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KACzB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { AppNotification } from "../types";
|
|
3
|
+
export declare function createNotificationsApi(http: HttpClient): {
|
|
4
|
+
list: (page?: number, pageSize?: number) => Promise<{
|
|
5
|
+
data: AppNotification[];
|
|
6
|
+
pagination: {
|
|
7
|
+
page: number;
|
|
8
|
+
page_size: number;
|
|
9
|
+
total_entries: number;
|
|
10
|
+
total_pages: number;
|
|
11
|
+
};
|
|
12
|
+
}>;
|
|
13
|
+
unreadCount: () => Promise<number>;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/api/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU;;cAIzC,eAAe,EAAE;oBACX;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE;;;EAQlG"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNotificationsApi = createNotificationsApi;
|
|
4
|
+
function createNotificationsApi(http) {
|
|
5
|
+
return {
|
|
6
|
+
list: (page = 1, pageSize = 20) => http.get(`/notifications?page=${page}&page_size=${pageSize}`),
|
|
7
|
+
unreadCount: () => http
|
|
8
|
+
.get("/notifications/unread-count")
|
|
9
|
+
.then((r) => r.data.count),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/api/notifications.ts"],"names":[],"mappings":";;AAGA,wDAaC;AAbD,SAAgB,sBAAsB,CAAC,IAAgB;IACrD,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAChC,IAAI,CAAC,GAAG,CAGL,uBAAuB,IAAI,cAAc,QAAQ,EAAE,CAAC;QAEzD,WAAW,EAAE,GAAG,EAAE,CAChB,IAAI;aACD,GAAG,CAA8B,6BAA6B,CAAC;aAC/D,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;KAC/B,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HttpClient } from "../http";
|
|
2
|
+
import type { UploadUrlResult } from "../types";
|
|
3
|
+
export declare function createUploadApi(http: HttpClient): {
|
|
4
|
+
getUploadUrl: (fileName: string, contentType: string, purpose?: "chat" | "avatar" | "kyc") => Promise<UploadUrlResult>;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=upload.d.ts.map
|