@antzsoft/chat-core 1.0.8 → 1.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 +53 -6
- package/dist/index.cjs +37 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +36 -10
- package/dist/index.js.map +1 -1
- package/docs/integration-guide.html +247 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -733,7 +733,7 @@ import { messagesApi } from '@antzsoft/chat-core';
|
|
|
733
733
|
| `unpin` | `(messageId: string) => Promise<Message>` | Unpin a message. |
|
|
734
734
|
| `getPinned` | `(conversationId: string) => Promise<Message[]>` | List pinned messages in a conversation. |
|
|
735
735
|
|
|
736
|
-
> **Delete permissions** — `delete()` (for everyone) requires either: the message belongs to the current user AND was sent within the delete window, OR the current user is a group admin. The server default window is **
|
|
736
|
+
> **Delete permissions** — `delete()` (for everyone) requires either: the message belongs to the current user AND was sent within the delete window, OR the current user is a group admin. The server default window is **216,000 s (60 hours)** when `conversation.settings.messageConfig.deleteWindowSeconds` is not set. DMs have no admin role — only the sender can delete for everyone in a DM. `deleteForMe()` is always allowed for any message. See the [integration guide — Edit & Delete section](docs/integration-guide.html#step-edit) for a ready-to-use `getDeleteOptions()` helper.
|
|
737
737
|
|
|
738
738
|
```typescript
|
|
739
739
|
interface ListMessagesParams {
|
|
@@ -852,7 +852,7 @@ import { conversationsApi } from '@antzsoft/chat-core';
|
|
|
852
852
|
| `updateParticipantRole` | `(conversationId: string, userId: string, role: 'admin' \| 'member') => Promise<Conversation>` | Promote or demote a participant. |
|
|
853
853
|
| `mute` | `(conversationId: string, mutedUntil?: string) => Promise<void>` | Mute notifications. Pass an ISO date string to mute until a specific time. |
|
|
854
854
|
| `unmute` | `(conversationId: string) => Promise<void>` | Unmute a conversation. |
|
|
855
|
-
| `pin` | `(conversationId: string) => Promise<void>` | Pin a conversation to the top of the list. |
|
|
855
|
+
| `pin` | `(conversationId: string) => Promise<void>` | Pin a conversation to the top of the list. Max 5 pins — server returns `400` if the limit is reached. |
|
|
856
856
|
| `unpin` | `(conversationId: string) => Promise<void>` | Unpin a conversation. |
|
|
857
857
|
| `leave` | `(conversationId: string) => Promise<void>` | Leave a group conversation. |
|
|
858
858
|
| `getMembers` | `(conversationId: string) => Promise<User[]>` | Fetch full user profiles for all participants. |
|
|
@@ -1554,6 +1554,33 @@ console.log(user.displayName, user.externalId);
|
|
|
1554
1554
|
|
|
1555
1555
|
---
|
|
1556
1556
|
|
|
1557
|
+
### App Config API (`appConfigApi`)
|
|
1558
|
+
|
|
1559
|
+
Returns server-side configuration constants. Call once on init and cache the result.
|
|
1560
|
+
|
|
1561
|
+
```typescript
|
|
1562
|
+
import { appConfigApi } from '@antzsoft/chat-core';
|
|
1563
|
+
```
|
|
1564
|
+
|
|
1565
|
+
| Method | Signature | Description |
|
|
1566
|
+
|---|---|---|
|
|
1567
|
+
| `get` | `() => Promise<AppConfig>` | Fetch app-level config from the server. |
|
|
1568
|
+
|
|
1569
|
+
```typescript
|
|
1570
|
+
interface AppConfig {
|
|
1571
|
+
maxPinnedConversations: number; // currently 5
|
|
1572
|
+
}
|
|
1573
|
+
```
|
|
1574
|
+
|
|
1575
|
+
```typescript
|
|
1576
|
+
const config = await appConfigApi.get();
|
|
1577
|
+
// Use config.maxPinnedConversations to gate the pin UI
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
> The SDK caches this under the `['app-config']` React Query key with `staleTime: Infinity` — it is only fetched once per session. `useConversations()` exposes `maxPinnedConversations` directly.
|
|
1581
|
+
|
|
1582
|
+
---
|
|
1583
|
+
|
|
1557
1584
|
### Socket
|
|
1558
1585
|
|
|
1559
1586
|
#### Connection management
|
|
@@ -1663,7 +1690,7 @@ All emit methods that have server responses use a 5-second ack timeout and retur
|
|
|
1663
1690
|
| `leaveRoom` | `(conversationId: string) => void` | Leave a conversation room. Fire-and-forget. |
|
|
1664
1691
|
| `sendMessage` | `(payload: SendMessagePayload) => Promise<unknown>` | Send a message. Ack-based. |
|
|
1665
1692
|
| `updateMessage` | `(messageId: string, text: string) => Promise<unknown>` | Edit a message. Ack-based. |
|
|
1666
|
-
| `deleteMessage` | `(messageId: string) => Promise<unknown>` | Delete a message for everyone. Own messages must be within the delete window (default
|
|
1693
|
+
| `deleteMessage` | `(messageId: string) => Promise<unknown>` | Delete a message for everyone. Own messages must be within the delete window (default 60 hours); group admins can delete any message with no time restriction. Ack-based. |
|
|
1667
1694
|
| `deleteMessageForMe` | `(messageId: string) => Promise<unknown>` | Hide a message for the current user only. Ack-based. |
|
|
1668
1695
|
| `addReaction` | `(messageId: string, emoji: string) => Promise<unknown>` | Add a reaction. Ack-based. |
|
|
1669
1696
|
| `removeReaction` | `(messageId: string, emoji: string) => Promise<unknown>` | Remove a reaction. Ack-based. |
|
|
@@ -2086,6 +2113,12 @@ interface Conversation {
|
|
|
2086
2113
|
interface ConversationSettings {
|
|
2087
2114
|
onlyAdminsCanMessage?: boolean;
|
|
2088
2115
|
onlyAdminsCanAddMembers?: boolean;
|
|
2116
|
+
messageConfig?: MessageConfig;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
interface MessageConfig {
|
|
2120
|
+
editWindowSeconds?: number;
|
|
2121
|
+
deleteWindowSeconds?: number;
|
|
2089
2122
|
}
|
|
2090
2123
|
```
|
|
2091
2124
|
|
|
@@ -2350,12 +2383,26 @@ document.querySelectorAll('[data-conv-id]').forEach((el) => {
|
|
|
2350
2383
|
|
|
2351
2384
|
## Changelog
|
|
2352
2385
|
|
|
2386
|
+
### v1.1.0
|
|
2387
|
+
- **Fix: 500 error on device token registration** — Registering a push token that was previously registered under a different user or device ID (e.g. after app reinstall, account switch, or UUID rotation) now succeeds. The stale token record is removed before the upsert, preventing a duplicate key violation on the global `token_unique` index.
|
|
2388
|
+
- **Fix: 500 error on remove participant** — Removing a participant no longer errors for the removed user's subsequent actions. Message stops and socket events now correctly target only remaining active members.
|
|
2389
|
+
- **Fix: Leave group now works for removed members** — Calling `conversationsApi.leave()` after being removed from a group (kicked by admin) now hides the conversation from the user's list instead of returning a 403. Previously, removed members (`isActive: false`) were blocked by the active-participant guard.
|
|
2390
|
+
- **Fix: Group auto-disbands when last member leaves or is removed** — When the last active participant leaves or is removed, the conversation is now marked inactive (`isActive: false`) automatically. Previously the group persisted as an orphan with zero members.
|
|
2391
|
+
- **Fix: `lastMessage` content not updating after message edit** — Editing a message now correctly updates `conversation.lastMessage.contentPreview` if the edited message is the current last message.
|
|
2392
|
+
- **Fix: Reply attachment preview shows `[Attachment]`** — When replying to a message that contains an attachment, the quoted preview now returns the filename if available, otherwise the file type (e.g. `image`, `video`). Clients should use the parent message's attachment data to render a visual preview for previewable types.
|
|
2393
|
+
- **Fix: Remove reaction returns error** — Removing a reaction that does not exist no longer throws a 500. The operation is now idempotent.
|
|
2394
|
+
- **New: Pin limit — max 5 pinned conversations** — Server enforces a limit of 5 pinned conversations per user and returns `400` if exceeded. A new `GET /app/config` endpoint and `appConfigApi.get()` return `{ maxPinnedConversations: 5 }` so clients can gate the UI before hitting the API. `useConversations()` (Web + RN) fetches config automatically, blocks the pin mutation if the limit is reached, and exposes `maxPinnedConversations`. RN `ConversationList` now supports long-press to pin/unpin.
|
|
2395
|
+
|
|
2396
|
+
### v1.0.9
|
|
2397
|
+
- **Socket reconnect resilience for `sendMessage`** — On Android, the OS suspends idle WebSocket connections during long audio recordings. `sendMessage` now waits up to 15 seconds for Socket.IO to auto-reconnect before sending, instead of immediately failing with "Socket not connected". Audio messages go through without error after upload.
|
|
2398
|
+
- **`settings` and `participantCount` now returned in conversation responses** — All conversation API responses and socket events (`conversation_created`, `conversation_updated`, etc.) now include `settings` (`onlyAdminsCanMessage`, `onlyAdminsCanAddMembers`, `messageConfig.editWindowSeconds`, `messageConfig.deleteWindowSeconds`) and `participantCount`. Previously these were server-side only. Fields were already in the `Conversation` type as optional.
|
|
2399
|
+
|
|
2353
2400
|
### v1.0.8
|
|
2354
2401
|
- **`duration` field in `SendMessageAttachment`** — Pass `duration` (seconds) when sending audio or video. Server now stores and returns it in `new_message` and message list responses. **Action required (RN):** Omitting it on React Native can crash native audio player libraries on the receiver side; always pass it for audio/video. Web is unaffected.
|
|
2355
2402
|
- **File compression** — `uploadBatch` now accepts optional `platformCompressFn` + `compressionConfig` args. Fully backward compatible — existing callers unchanged. Web/RN SDKs wire this in automatically; Node.js users can supply `nodeCompressFn` manually. No action required unless opting in on Node.
|
|
2356
|
-
- **Removed members keep read-only access** — Server behavior change. Removed participants stay in their conversation list and can read history but cannot write. Socket room membership ends immediately on removal.
|
|
2357
|
-
- **Admin delete is hide-only** — Server behavior change. Deleting a conversation sets `isHidden` for the requester only; other participants are unaffected.
|
|
2358
|
-
- **Fix: `lastMessage.status` stuck as `deleted`** — Server now explicitly resets `status: 'active'` on both REST and WebSocket paths when a new message is sent.
|
|
2403
|
+
- **Removed members keep read-only access** — Server behavior change. Removed participants stay in their conversation list and can read history but cannot write. Socket room membership ends immediately on removal.
|
|
2404
|
+
- **Admin delete is hide-only** — Server behavior change. Deleting a conversation sets `isHidden` for the requester only; other participants are unaffected.
|
|
2405
|
+
- **Fix: `lastMessage.status` stuck as `deleted`** — Server now explicitly resets `status: 'active'` on both REST and WebSocket paths when a new message is sent.
|
|
2359
2406
|
- **Fix: `duration` stored from sender payload** — `SendMessageAttachment.duration` is now persisted and echoed back. The `SendMessageAttachment` interface in this package is unchanged (field was already present as optional).
|
|
2360
2407
|
|
|
2361
2408
|
### v1.0.7
|
package/dist/index.cjs
CHANGED
|
@@ -94,6 +94,7 @@ var init_chat_store = __esm({
|
|
|
94
94
|
var src_exports = {};
|
|
95
95
|
__export(src_exports, {
|
|
96
96
|
AntzChatClient: () => AntzChatClient,
|
|
97
|
+
appConfigApi: () => appConfigApi,
|
|
97
98
|
authApi: () => authApi,
|
|
98
99
|
connectSocket: () => connectSocket,
|
|
99
100
|
conversationsApi: () => conversationsApi,
|
|
@@ -372,6 +373,14 @@ var authApi = {
|
|
|
372
373
|
}
|
|
373
374
|
};
|
|
374
375
|
|
|
376
|
+
// src/api/app-config.ts
|
|
377
|
+
var appConfigApi = {
|
|
378
|
+
async get() {
|
|
379
|
+
const { data } = await getApiClient().get("/app/config");
|
|
380
|
+
return data;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
375
384
|
// src/api/messages.ts
|
|
376
385
|
var messagesApi = {
|
|
377
386
|
async list(conversationId, params = {}) {
|
|
@@ -649,14 +658,7 @@ async function uploadBatch(files, platformUploadFn, conversationId, onProgress,
|
|
|
649
658
|
filename: f.name,
|
|
650
659
|
mimeType: f.type,
|
|
651
660
|
size: f.size,
|
|
652
|
-
conversationId
|
|
653
|
-
...f.compressed && {
|
|
654
|
-
metadata: {
|
|
655
|
-
compressed: true,
|
|
656
|
-
originalSize: f.originalSize,
|
|
657
|
-
compressionAlgorithm: f.compressionAlgorithm
|
|
658
|
-
}
|
|
659
|
-
}
|
|
661
|
+
conversationId
|
|
660
662
|
}));
|
|
661
663
|
const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);
|
|
662
664
|
const progressMap = {};
|
|
@@ -865,8 +867,32 @@ function refreshSocketAuth() {
|
|
|
865
867
|
|
|
866
868
|
// src/socket/emitters.ts
|
|
867
869
|
var ACK_TIMEOUT = 5e3;
|
|
868
|
-
|
|
869
|
-
|
|
870
|
+
var RECONNECT_WAIT_TIMEOUT = 15e3;
|
|
871
|
+
function waitForReconnect() {
|
|
872
|
+
return new Promise((resolve, reject) => {
|
|
873
|
+
const timer = setTimeout(() => {
|
|
874
|
+
unsubscribe();
|
|
875
|
+
reject(new Error("[AntzChat] Socket reconnect timeout"));
|
|
876
|
+
}, RECONNECT_WAIT_TIMEOUT);
|
|
877
|
+
const unsubscribe = onSocketStatus((status) => {
|
|
878
|
+
if (status === "connected") {
|
|
879
|
+
clearTimeout(timer);
|
|
880
|
+
unsubscribe();
|
|
881
|
+
resolve();
|
|
882
|
+
} else if (status === "error") {
|
|
883
|
+
clearTimeout(timer);
|
|
884
|
+
unsubscribe();
|
|
885
|
+
reject(new Error("[AntzChat] Socket reconnect failed"));
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
async function withAck(event, payload) {
|
|
891
|
+
let socket = tryGetSocket();
|
|
892
|
+
if (!socket) {
|
|
893
|
+
await waitForReconnect();
|
|
894
|
+
socket = tryGetSocket();
|
|
895
|
+
}
|
|
870
896
|
if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));
|
|
871
897
|
return new Promise((resolve, reject) => {
|
|
872
898
|
const timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);
|
|
@@ -1084,6 +1110,7 @@ var AntzChatClient = class {
|
|
|
1084
1110
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1085
1111
|
0 && (module.exports = {
|
|
1086
1112
|
AntzChatClient,
|
|
1113
|
+
appConfigApi,
|
|
1087
1114
|
authApi,
|
|
1088
1115
|
connectSocket,
|
|
1089
1116
|
conversationsApi,
|