@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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { formatTime, formatDateSeparator, shouldShowDateSeparator } from "./formatters";
|
|
2
|
+
export { ChatClient } from "./client";
|
|
3
|
+
export { TypedEventEmitter } from "./events";
|
|
4
|
+
export { HttpError } from "./http";
|
|
5
|
+
export { retry } from "./retry";
|
|
6
|
+
export { toMessage, toConversation } from "./socket";
|
|
7
|
+
export type { Message, TempMessage, MessageDirection, MessageChannel, MessageType, MessageStatus, Conversation, ConversationStatus, Participant, ParticipantUser, BusinessSummary, Contact, ContactType, ContactContext, AuthTokens, LoginParams, RegisterParams, OtpSendParams, OtpVerifyParams, OidcParams, AppNotification, PaginationMeta, MessageListMeta, UploadUrlResult, PresenceUser, ChatStorage, MMKVStorage, ChatEventMap, ChatEventName, ChatClientOptions, PushEventResult, MessagesLoadedPayload, TypingPayload, MessagesReadPayload, MessagesDeliveredPayload, PresencePayload, ConversationCreatedPayload, ConversationUpdatedPayload, UnreadCountPayload, } from "./types";
|
|
8
|
+
export { createMMKVStorageAdapter } from "./types";
|
|
9
|
+
export type { RetryOptions } from "./retry";
|
|
10
|
+
export type { StartChatParams, } from "./api/conversations";
|
|
11
|
+
export { useChat, useConversations, useContacts, } from "./react";
|
|
12
|
+
export type { UseChatResult, UseConversationsResult, UseContactsResult, } from "./react";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEvF,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEpD,YAAY,EACV,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,eAAe,EACf,OAAO,EACP,WAAW,EACX,cAAc,EACd,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,0BAA0B,EAC1B,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAA;AAElD,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC3C,YAAY,EACV,eAAe,GAChB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,WAAW,GACZ,MAAM,SAAS,CAAA;AAEhB,YAAY,EACV,aAAa,EACb,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,SAAS,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useContacts = exports.useConversations = exports.useChat = exports.createMMKVStorageAdapter = exports.toConversation = exports.toMessage = exports.retry = exports.HttpError = exports.TypedEventEmitter = exports.ChatClient = exports.shouldShowDateSeparator = exports.formatDateSeparator = exports.formatTime = void 0;
|
|
4
|
+
var formatters_1 = require("./formatters");
|
|
5
|
+
Object.defineProperty(exports, "formatTime", { enumerable: true, get: function () { return formatters_1.formatTime; } });
|
|
6
|
+
Object.defineProperty(exports, "formatDateSeparator", { enumerable: true, get: function () { return formatters_1.formatDateSeparator; } });
|
|
7
|
+
Object.defineProperty(exports, "shouldShowDateSeparator", { enumerable: true, get: function () { return formatters_1.shouldShowDateSeparator; } });
|
|
8
|
+
var client_1 = require("./client");
|
|
9
|
+
Object.defineProperty(exports, "ChatClient", { enumerable: true, get: function () { return client_1.ChatClient; } });
|
|
10
|
+
var events_1 = require("./events");
|
|
11
|
+
Object.defineProperty(exports, "TypedEventEmitter", { enumerable: true, get: function () { return events_1.TypedEventEmitter; } });
|
|
12
|
+
var http_1 = require("./http");
|
|
13
|
+
Object.defineProperty(exports, "HttpError", { enumerable: true, get: function () { return http_1.HttpError; } });
|
|
14
|
+
var retry_1 = require("./retry");
|
|
15
|
+
Object.defineProperty(exports, "retry", { enumerable: true, get: function () { return retry_1.retry; } });
|
|
16
|
+
var socket_1 = require("./socket");
|
|
17
|
+
Object.defineProperty(exports, "toMessage", { enumerable: true, get: function () { return socket_1.toMessage; } });
|
|
18
|
+
Object.defineProperty(exports, "toConversation", { enumerable: true, get: function () { return socket_1.toConversation; } });
|
|
19
|
+
var types_1 = require("./types");
|
|
20
|
+
Object.defineProperty(exports, "createMMKVStorageAdapter", { enumerable: true, get: function () { return types_1.createMMKVStorageAdapter; } });
|
|
21
|
+
var react_1 = require("./react");
|
|
22
|
+
Object.defineProperty(exports, "useChat", { enumerable: true, get: function () { return react_1.useChat; } });
|
|
23
|
+
Object.defineProperty(exports, "useConversations", { enumerable: true, get: function () { return react_1.useConversations; } });
|
|
24
|
+
Object.defineProperty(exports, "useContacts", { enumerable: true, get: function () { return react_1.useContacts; } });
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAAuF;AAA9E,wGAAA,UAAU,OAAA;AAAE,iHAAA,mBAAmB,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAEjE,mCAAqC;AAA5B,oGAAA,UAAU,OAAA;AACnB,mCAA4C;AAAnC,2GAAA,iBAAiB,OAAA;AAC1B,+BAAkC;AAAzB,iGAAA,SAAS,OAAA;AAClB,iCAA+B;AAAtB,8FAAA,KAAK,OAAA;AAEd,mCAAoD;AAA3C,mGAAA,SAAS,OAAA;AAAE,wGAAA,cAAc,OAAA;AA4ClC,iCAAkD;AAAzC,iHAAA,wBAAwB,OAAA;AAOjC,iCAIgB;AAHd,gGAAA,OAAO,OAAA;AACP,yGAAA,gBAAgB,OAAA;AAChB,oGAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the event-driven chat client.
|
|
3
|
+
*
|
|
4
|
+
* These tests connect to localhost:4000 and verify that all data flows
|
|
5
|
+
* through WebSocket events — no REST polling, no manual data invalidation.
|
|
6
|
+
*
|
|
7
|
+
* Usage: npx vitest run src/integration.test.ts
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=integration.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Integration tests for the event-driven chat client.
|
|
4
|
+
*
|
|
5
|
+
* These tests connect to localhost:4000 and verify that all data flows
|
|
6
|
+
* through WebSocket events — no REST polling, no manual data invalidation.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx vitest run src/integration.test.ts
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const vitest_1 = require("vitest");
|
|
12
|
+
const client_1 = require("./client");
|
|
13
|
+
// ─── Config ──────────────────────────────────────────────────
|
|
14
|
+
const BASE_URL = "http://localhost:4000/api/v1";
|
|
15
|
+
const WS_URL = "ws://localhost:4000/ws";
|
|
16
|
+
// @ts-ignore - `process` is available in vitest environment
|
|
17
|
+
const ENV = typeof process !== "undefined" ? process.env : {};
|
|
18
|
+
const RIDER_TOKEN = ENV.RIDER_TOKEN ??
|
|
19
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVyaWMubTQ1MTFAZ21haWwuY29tIiwiZXhwIjoxNzgzNDMyOTM2LCJpYXQiOjE3ODMzNDY1MzYsImlzX3N1cGVyX2FkbWluIjpmYWxzZSwianRpIjoiZDE0ZGM3ZjI2MTNiODU1N2JiZWRiYWQwN2JhN2JiZGMiLCJsb2dpbl9tZXRob2QiOiJlbWFpbF9wYXNzd29yZCIsInN1YiI6IjA0ZDAwNjhlLThlM2QtNDczMS04NmM4LTQ1NmMxOTU3NzNhMiIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX3R5cGUiOiJwdWJsaWMifQ.J-o6YBE_nCkuEjdQT7lD7fMaWb6Z-v6WV75D2Xug6bQ";
|
|
20
|
+
const BUSINESS_TOKEN = ENV.BUSINESS_TOKEN ??
|
|
21
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImVyZWNveC5jZW9AZ21haWwuY29tIiwiZXhwIjoxNzgzNDMwNjQ2LCJpYXQiOjE3ODMzNDQyNDYsImlzX3N1cGVyX2FkbWluIjpmYWxzZSwianRpIjoiNTVmNzFiMTcwMWIxMGZiM2FlYTNiNWIxMTllNWQxOTIiLCJsb2dpbl9tZXRob2QiOiJlbWFpbF9wYXNzd29yZCIsInN1YiI6ImZkZGVhNTljLTA0ZDItNGUxOS04M2NmLTE5MzNmNWI3OTQzMCIsInR5cGUiOiJhY2Nlc3MiLCJ1c2VyX3R5cGUiOiJwdWJsaWMifQ._kOMrA_fOTXEnsqNXH8DTxG3jfPtKh9UJdL4W0863V8";
|
|
22
|
+
const RIDER_USER_ID = "04d0068e-8e3d-4731-86c8-456c195773a2";
|
|
23
|
+
const BUSINESS_USER_ID = "fddea59c-04d2-4e19-83cf-1933f5b79430";
|
|
24
|
+
// ─── In-memory storage adapter ────────────────────────────────
|
|
25
|
+
class MapStorage {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.map = new Map();
|
|
28
|
+
}
|
|
29
|
+
async getItem(key) {
|
|
30
|
+
const v = this.map.get(key);
|
|
31
|
+
return v ? JSON.parse(v) : null;
|
|
32
|
+
}
|
|
33
|
+
async setItem(key, value) {
|
|
34
|
+
this.map.set(key, JSON.stringify(value));
|
|
35
|
+
}
|
|
36
|
+
async removeItem(key) {
|
|
37
|
+
this.map.delete(key);
|
|
38
|
+
}
|
|
39
|
+
async clear() {
|
|
40
|
+
this.map.clear();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ─── Event helper ────────────────────────────────────────────
|
|
44
|
+
function waitForEvent(client, event, timeoutMs = 8000) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const timer = setTimeout(() => {
|
|
47
|
+
reject(new Error(`Timed out waiting for event "${event}"`));
|
|
48
|
+
}, timeoutMs);
|
|
49
|
+
const unsub = client.on(event, (payload) => {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
unsub();
|
|
52
|
+
resolve(payload);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// ─── Shared helpers ──────────────────────────────────────────
|
|
57
|
+
function makeClient() {
|
|
58
|
+
return new client_1.ChatClient({
|
|
59
|
+
baseUrl: BASE_URL,
|
|
60
|
+
wsUrl: WS_URL,
|
|
61
|
+
storage: new MapStorage(),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// ─── Test suite ──────────────────────────────────────────────
|
|
65
|
+
let riderClient;
|
|
66
|
+
let businessClient;
|
|
67
|
+
let convId;
|
|
68
|
+
(0, vitest_1.describe)("teincfood-chat-client (event-driven)", () => {
|
|
69
|
+
(0, vitest_1.beforeAll)(async () => {
|
|
70
|
+
// Initialize both clients
|
|
71
|
+
riderClient = makeClient();
|
|
72
|
+
riderClient.setCurrentUserId(RIDER_USER_ID);
|
|
73
|
+
await riderClient.start({
|
|
74
|
+
access_token: RIDER_TOKEN,
|
|
75
|
+
refresh_token: RIDER_TOKEN,
|
|
76
|
+
});
|
|
77
|
+
businessClient = makeClient();
|
|
78
|
+
businessClient.setCurrentUserId(BUSINESS_USER_ID);
|
|
79
|
+
await businessClient.start({
|
|
80
|
+
access_token: BUSINESS_TOKEN,
|
|
81
|
+
refresh_token: BUSINESS_TOKEN,
|
|
82
|
+
});
|
|
83
|
+
// Find or create a conversation between these two users
|
|
84
|
+
const riderInbox = await riderClient.conversations.getInbox();
|
|
85
|
+
const sharedConv = riderInbox.find((c) => c.participants?.some((p) => p.user_id === BUSINESS_USER_ID));
|
|
86
|
+
if (sharedConv) {
|
|
87
|
+
convId = sharedConv.id;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const start = await riderClient.conversations.startChat({
|
|
91
|
+
contact_type: "user",
|
|
92
|
+
contact_id: BUSINESS_USER_ID,
|
|
93
|
+
});
|
|
94
|
+
convId = start.id;
|
|
95
|
+
}
|
|
96
|
+
console.log(`[setup] conversation: ${convId}`);
|
|
97
|
+
});
|
|
98
|
+
// ─── Live event tests ─────────────────────────────
|
|
99
|
+
(0, vitest_1.it)("both clients join the conversation and receive presence", async () => {
|
|
100
|
+
const riderPresence = waitForEvent(riderClient, "presence");
|
|
101
|
+
const bizPresence = waitForEvent(businessClient, "presence");
|
|
102
|
+
await riderClient.joinConversation(convId);
|
|
103
|
+
await businessClient.joinConversation(convId);
|
|
104
|
+
const rp = await riderPresence;
|
|
105
|
+
(0, vitest_1.expect)(rp.conversationId).toBe(convId);
|
|
106
|
+
(0, vitest_1.expect)(rp.users.length).toBeGreaterThan(0);
|
|
107
|
+
console.log(`[live] rider received presence: ${rp.users.length} users`);
|
|
108
|
+
const bp = await bizPresence;
|
|
109
|
+
(0, vitest_1.expect)(bp.conversationId).toBe(convId);
|
|
110
|
+
console.log(`[live] business received presence: ${bp.users.length} users`);
|
|
111
|
+
});
|
|
112
|
+
(0, vitest_1.it)("rider sends message and receives message:sent confirmation", async () => {
|
|
113
|
+
const promise = waitForEvent(riderClient, "message:sent");
|
|
114
|
+
riderClient.sendMessage(convId, "Hello from event-driven test!");
|
|
115
|
+
const confirmed = await promise;
|
|
116
|
+
(0, vitest_1.expect)(confirmed).toBeDefined();
|
|
117
|
+
(0, vitest_1.expect)(confirmed.content).toContain("Hello from event-driven test!");
|
|
118
|
+
(0, vitest_1.expect)(confirmed.conversation_id).toBe(convId);
|
|
119
|
+
console.log(`[live] message sent: id=${confirmed.id?.slice(0, 8)} status=${confirmed.status}`);
|
|
120
|
+
});
|
|
121
|
+
(0, vitest_1.it)("business receives the message via WebSocket event", async () => {
|
|
122
|
+
// Listen for ANY incoming message, skip stale events
|
|
123
|
+
const promise = new Promise((resolve) => {
|
|
124
|
+
const unsub = businessClient.on("message:received", (msg) => {
|
|
125
|
+
if (msg.content.includes("Second message")) {
|
|
126
|
+
unsub();
|
|
127
|
+
resolve(msg);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
riderClient.sendMessage(convId, "Second message — live delivery check");
|
|
132
|
+
const received = await promise;
|
|
133
|
+
(0, vitest_1.expect)(received.content).toContain("Second message");
|
|
134
|
+
(0, vitest_1.expect)(received.conversation_id).toBe(convId);
|
|
135
|
+
console.log(`[live] business received: "${received.content.slice(0, 60)}"`);
|
|
136
|
+
});
|
|
137
|
+
(0, vitest_1.it)("auto-mark-read notifies the sender", async () => {
|
|
138
|
+
// Business is already joined from previous test, so auto-mark-read
|
|
139
|
+
// fires on join. For this test, have rider send then business re-joins.
|
|
140
|
+
const promise = waitForEvent(riderClient, "messages_read");
|
|
141
|
+
riderClient.sendMessage(convId, "Read receipt test message");
|
|
142
|
+
// Wait for send to complete
|
|
143
|
+
await waitForEvent(riderClient, "message:sent");
|
|
144
|
+
// Business leaves and re-joins to trigger auto-mark-read
|
|
145
|
+
businessClient.leaveConversation(convId);
|
|
146
|
+
await businessClient.joinConversation(convId);
|
|
147
|
+
const receipt = await promise;
|
|
148
|
+
(0, vitest_1.expect)(receipt.conversationId).toBe(convId);
|
|
149
|
+
(0, vitest_1.expect)(receipt.userId).toBe(BUSINESS_USER_ID);
|
|
150
|
+
(0, vitest_1.expect)(receipt.readAt).toBeTruthy();
|
|
151
|
+
console.log(`[live] rider received read receipt from business at ${receipt.readAt}`);
|
|
152
|
+
});
|
|
153
|
+
// ─── Error handling ───────────────────────────────
|
|
154
|
+
(0, vitest_1.it)("returns auth:expired for an invalid token", async () => {
|
|
155
|
+
const badClient = makeClient();
|
|
156
|
+
badClient.setCurrentUserId("00000000-0000-0000-0000-000000000000");
|
|
157
|
+
const promise = waitForEvent(badClient, "auth:expired", 5000);
|
|
158
|
+
await badClient.start({
|
|
159
|
+
access_token: "bad_token",
|
|
160
|
+
refresh_token: "bad_token",
|
|
161
|
+
});
|
|
162
|
+
// Try an operation that triggers an auth check
|
|
163
|
+
try {
|
|
164
|
+
await badClient.loadInbox();
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Expected
|
|
168
|
+
}
|
|
169
|
+
const result = await promise;
|
|
170
|
+
(0, vitest_1.expect)(result).toBeUndefined();
|
|
171
|
+
console.log("[error] auth:expired fired correctly");
|
|
172
|
+
});
|
|
173
|
+
(0, vitest_1.it)("handles errors gracefully", async () => {
|
|
174
|
+
try {
|
|
175
|
+
await riderClient.conversations.getConversation("00000000-0000-0000-0000-000000000000");
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
const err = e;
|
|
179
|
+
(0, vitest_1.expect)(err.status).toBe(404);
|
|
180
|
+
console.log(`[error] 404 handled: ${err.message}`);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../src/integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Message, TempMessage } from "./types";
|
|
2
|
+
import { TypedEventEmitter } from "./events";
|
|
3
|
+
export declare class OptimisticEngine {
|
|
4
|
+
private pending;
|
|
5
|
+
private emitter;
|
|
6
|
+
constructor(emitter: TypedEventEmitter);
|
|
7
|
+
/** Create an optimistic message, emit 'message:sending', return it. */
|
|
8
|
+
create(conversationId: string, senderId: string, body: string, metadata?: Record<string, unknown> | null): TempMessage;
|
|
9
|
+
/** Mark all pending messages for this conversation as failed. */
|
|
10
|
+
failConversation(conversationId: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Reconcile — match a server-acknowledged message against pending temps.
|
|
13
|
+
* Matching strategy: same conversation + same content + created within 10s window.
|
|
14
|
+
* Returns the matched temp's _clientId (if any) for callers to identify it.
|
|
15
|
+
*/
|
|
16
|
+
reconcile(serverMsg: Message): string | null;
|
|
17
|
+
/** Return all pending messages for a conversation. */
|
|
18
|
+
getPending(conversationId: string): TempMessage[];
|
|
19
|
+
/** Remove a single pending message by client ID. */
|
|
20
|
+
removeByClientId(clientId: string): void;
|
|
21
|
+
/** Return all pending messages across all conversations. */
|
|
22
|
+
getAll(): TempMessage[];
|
|
23
|
+
/** Clear all pending (e.g., on disconnect). */
|
|
24
|
+
clear(): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=optimistic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimistic.d.ts","sourceRoot":"","sources":["../src/optimistic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAO5C,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,OAAO,CAAmB;gBAEtB,OAAO,EAAE,iBAAiB;IAItC,uEAAuE;IACvE,MAAM,CACJ,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACxC,WAAW;IAwBd,iEAAiE;IACjE,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAU9C;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAiB5C,sDAAsD;IACtD,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE;IAMjD,oDAAoD;IACpD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIxC,4DAA4D;IAC5D,MAAM,IAAI,WAAW,EAAE;IAIvB,+CAA+C;IAC/C,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OptimisticEngine = void 0;
|
|
4
|
+
let clientIdCounter = 0;
|
|
5
|
+
function nextClientId() {
|
|
6
|
+
return `opt_${Date.now()}_${++clientIdCounter}`;
|
|
7
|
+
}
|
|
8
|
+
class OptimisticEngine {
|
|
9
|
+
constructor(emitter) {
|
|
10
|
+
this.pending = new Map();
|
|
11
|
+
this.emitter = emitter;
|
|
12
|
+
}
|
|
13
|
+
/** Create an optimistic message, emit 'message:sending', return it. */
|
|
14
|
+
create(conversationId, senderId, body, metadata) {
|
|
15
|
+
const now = new Date().toISOString();
|
|
16
|
+
const msg = {
|
|
17
|
+
_clientId: nextClientId(),
|
|
18
|
+
id: null,
|
|
19
|
+
content: body,
|
|
20
|
+
sender_id: senderId,
|
|
21
|
+
conversation_id: conversationId,
|
|
22
|
+
direction: "inbound",
|
|
23
|
+
channel: "app",
|
|
24
|
+
message_type: "text",
|
|
25
|
+
status: "sending",
|
|
26
|
+
external_id: null,
|
|
27
|
+
metadata: metadata ?? null,
|
|
28
|
+
replied_by_id: null,
|
|
29
|
+
read: false,
|
|
30
|
+
sent_at: now,
|
|
31
|
+
inserted_at: now,
|
|
32
|
+
};
|
|
33
|
+
this.pending.set(msg._clientId, msg);
|
|
34
|
+
this.emitter.emit("message:sending", msg);
|
|
35
|
+
return msg;
|
|
36
|
+
}
|
|
37
|
+
/** Mark all pending messages for this conversation as failed. */
|
|
38
|
+
failConversation(conversationId) {
|
|
39
|
+
for (const [cid, msg] of this.pending) {
|
|
40
|
+
if (msg.conversation_id === conversationId) {
|
|
41
|
+
msg.status = "failed";
|
|
42
|
+
this.emitter.emit("message:failed", msg);
|
|
43
|
+
this.pending.delete(cid);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Reconcile — match a server-acknowledged message against pending temps.
|
|
49
|
+
* Matching strategy: same conversation + same content + created within 10s window.
|
|
50
|
+
* Returns the matched temp's _clientId (if any) for callers to identify it.
|
|
51
|
+
*/
|
|
52
|
+
reconcile(serverMsg) {
|
|
53
|
+
const serverTime = new Date(serverMsg.inserted_at).getTime();
|
|
54
|
+
for (const [cid, temp] of this.pending) {
|
|
55
|
+
if (temp.conversation_id !== serverMsg.conversation_id)
|
|
56
|
+
continue;
|
|
57
|
+
if (temp.content !== serverMsg.content)
|
|
58
|
+
continue;
|
|
59
|
+
const tempTime = new Date(temp.inserted_at).getTime();
|
|
60
|
+
if (Math.abs(serverTime - tempTime) > 10000)
|
|
61
|
+
continue;
|
|
62
|
+
this.pending.delete(cid);
|
|
63
|
+
return cid;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/** Return all pending messages for a conversation. */
|
|
68
|
+
getPending(conversationId) {
|
|
69
|
+
return Array.from(this.pending.values()).filter((m) => m.conversation_id === conversationId);
|
|
70
|
+
}
|
|
71
|
+
/** Remove a single pending message by client ID. */
|
|
72
|
+
removeByClientId(clientId) {
|
|
73
|
+
this.pending.delete(clientId);
|
|
74
|
+
}
|
|
75
|
+
/** Return all pending messages across all conversations. */
|
|
76
|
+
getAll() {
|
|
77
|
+
return Array.from(this.pending.values());
|
|
78
|
+
}
|
|
79
|
+
/** Clear all pending (e.g., on disconnect). */
|
|
80
|
+
clear() {
|
|
81
|
+
this.pending.clear();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.OptimisticEngine = OptimisticEngine;
|
|
85
|
+
//# sourceMappingURL=optimistic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimistic.js","sourceRoot":"","sources":["../src/optimistic.ts"],"names":[],"mappings":"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"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ChatClient } from "./client";
|
|
2
|
+
import type { Message, Conversation, Contact, ContactContext, TempMessage } from "./types";
|
|
3
|
+
export interface UseChatResult {
|
|
4
|
+
messages: Message[];
|
|
5
|
+
send: (body: string, metadata?: Record<string, unknown> | null) => TempMessage;
|
|
6
|
+
loadMore: () => Promise<void>;
|
|
7
|
+
sendTyping: () => void;
|
|
8
|
+
markRead: () => void;
|
|
9
|
+
hasMore: boolean;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
error: Error | null;
|
|
12
|
+
}
|
|
13
|
+
export declare function useChat(client: ChatClient, conversationId: string | null): UseChatResult;
|
|
14
|
+
export interface UseConversationsResult {
|
|
15
|
+
conversations: Conversation[];
|
|
16
|
+
loading: boolean;
|
|
17
|
+
refresh: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare function useConversations(client: ChatClient): UseConversationsResult;
|
|
20
|
+
export interface UseContactsResult {
|
|
21
|
+
contacts: Contact[];
|
|
22
|
+
loading: boolean;
|
|
23
|
+
refresh: () => Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare function useContacts(client: ChatClient, context?: ContactContext, businessId?: string): UseContactsResult;
|
|
26
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,OAAO,EACP,cAAc,EACd,WAAW,EACZ,MAAM,SAAS,CAAA;AA+BhB,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAAK,WAAW,CAAA;IAC9E,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,UAAU,EAAE,MAAM,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CACpB;AAED,wBAAgB,OAAO,CACrB,MAAM,EAAE,UAAU,EAClB,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,aAAa,CA6Hf;AAMD,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,GACjB,sBAAsB,CA2CxB;AAMD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAED,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,cAAc,EACxB,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,CA6CnB"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useChat = useChat;
|
|
4
|
+
exports.useConversations = useConversations;
|
|
5
|
+
exports.useContacts = useContacts;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
// ════════════════════════════════════════════════════════════
|
|
8
|
+
// Helper — build a reactive subscribe/store tuple from
|
|
9
|
+
// an external source.
|
|
10
|
+
// ════════════════════════════════════════════════════════════
|
|
11
|
+
function externalStore(getSnapshot) {
|
|
12
|
+
let snapshot = getSnapshot();
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
const subscribe = (cb) => {
|
|
15
|
+
listeners.add(cb);
|
|
16
|
+
return () => listeners.delete(cb);
|
|
17
|
+
};
|
|
18
|
+
const emit = () => {
|
|
19
|
+
const next = getSnapshot();
|
|
20
|
+
if (next !== snapshot) {
|
|
21
|
+
snapshot = next;
|
|
22
|
+
(0, react_1.startTransition)(() => listeners.forEach((fn) => fn()));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
return { subscribe, getSnapshot: () => snapshot, emit };
|
|
26
|
+
}
|
|
27
|
+
function useChat(client, conversationId) {
|
|
28
|
+
const loadingRef = (0, react_1.useRef)(false);
|
|
29
|
+
const offsetRef = (0, react_1.useRef)(0);
|
|
30
|
+
const hasMoreRef = (0, react_1.useRef)(true);
|
|
31
|
+
const errorRef = (0, react_1.useRef)(null);
|
|
32
|
+
const getMessages = (0, react_1.useCallback)(() => {
|
|
33
|
+
if (!conversationId)
|
|
34
|
+
return [];
|
|
35
|
+
const stored = client.store.getMessages(conversationId);
|
|
36
|
+
const pending = client.getPendingMessages(conversationId);
|
|
37
|
+
const pendingMap = new Map();
|
|
38
|
+
for (const p of pending) {
|
|
39
|
+
pendingMap.set(`pending_${p._clientId}`, p);
|
|
40
|
+
}
|
|
41
|
+
for (const m of stored) {
|
|
42
|
+
if (m.id)
|
|
43
|
+
pendingMap.delete(`pending_${m.id}`);
|
|
44
|
+
}
|
|
45
|
+
const all = [...stored, ...pendingMap.values()];
|
|
46
|
+
all.sort((a, b) => new Date(a.inserted_at).getTime() -
|
|
47
|
+
new Date(b.inserted_at).getTime());
|
|
48
|
+
return all;
|
|
49
|
+
}, [conversationId, client]);
|
|
50
|
+
const store = (0, react_1.useRef)(externalStore(getMessages)).current;
|
|
51
|
+
// Subscribe to relevant events + join conversation channel
|
|
52
|
+
(0, react_1.useEffect)(() => {
|
|
53
|
+
if (!conversationId)
|
|
54
|
+
return;
|
|
55
|
+
let cancelled = false;
|
|
56
|
+
(async () => {
|
|
57
|
+
try {
|
|
58
|
+
await client.joinConversation(conversationId);
|
|
59
|
+
if (!cancelled) {
|
|
60
|
+
// Load messages from REST as the authoritative source
|
|
61
|
+
await client.loadMessages(conversationId, 50, 0);
|
|
62
|
+
store.emit();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Channel join failed — REST calls still work via loadMessages
|
|
67
|
+
try {
|
|
68
|
+
if (!cancelled) {
|
|
69
|
+
await client.loadMessages(conversationId, 50, 0);
|
|
70
|
+
store.emit();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch { }
|
|
74
|
+
}
|
|
75
|
+
})();
|
|
76
|
+
const unsubs = [
|
|
77
|
+
client.on("message:sending", () => store.emit()),
|
|
78
|
+
client.on("message:sent", () => store.emit()),
|
|
79
|
+
client.on("message:received", () => store.emit()),
|
|
80
|
+
client.on("message:failed", () => store.emit()),
|
|
81
|
+
client.on("messages:loaded", (p) => {
|
|
82
|
+
if (p.conversationId === conversationId)
|
|
83
|
+
store.emit();
|
|
84
|
+
}),
|
|
85
|
+
client.on("messages_read", () => store.emit()),
|
|
86
|
+
client.on("messages_delivered", () => store.emit()),
|
|
87
|
+
];
|
|
88
|
+
return () => {
|
|
89
|
+
cancelled = true;
|
|
90
|
+
unsubs.forEach((fn) => fn());
|
|
91
|
+
client.leaveConversation(conversationId);
|
|
92
|
+
};
|
|
93
|
+
}, [conversationId, client, store]);
|
|
94
|
+
const messages = (0, react_1.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
95
|
+
const send = (0, react_1.useCallback)((body, metadata) => {
|
|
96
|
+
if (!conversationId)
|
|
97
|
+
throw new Error("No active conversation");
|
|
98
|
+
return client.sendMessage(conversationId, body, metadata);
|
|
99
|
+
}, [conversationId, client]);
|
|
100
|
+
const loadMore = (0, react_1.useCallback)(async () => {
|
|
101
|
+
if (!conversationId || !hasMoreRef.current || loadingRef.current)
|
|
102
|
+
return;
|
|
103
|
+
loadingRef.current = true;
|
|
104
|
+
errorRef.current = null;
|
|
105
|
+
try {
|
|
106
|
+
offsetRef.current += 50;
|
|
107
|
+
const result = await client.loadMessages(conversationId, 50, offsetRef.current);
|
|
108
|
+
hasMoreRef.current = result.data.length === 50;
|
|
109
|
+
store.emit();
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
errorRef.current = e instanceof Error ? e : new Error(String(e));
|
|
113
|
+
offsetRef.current -= 50;
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
loadingRef.current = false;
|
|
117
|
+
}
|
|
118
|
+
}, [conversationId, client, store]);
|
|
119
|
+
const sendTyping = (0, react_1.useCallback)(() => {
|
|
120
|
+
if (conversationId)
|
|
121
|
+
client.sendTyping(conversationId);
|
|
122
|
+
}, [conversationId, client]);
|
|
123
|
+
const markRead = (0, react_1.useCallback)(() => {
|
|
124
|
+
if (conversationId)
|
|
125
|
+
client.markRead(conversationId);
|
|
126
|
+
}, [conversationId, client]);
|
|
127
|
+
return {
|
|
128
|
+
messages,
|
|
129
|
+
send,
|
|
130
|
+
loadMore,
|
|
131
|
+
sendTyping,
|
|
132
|
+
markRead,
|
|
133
|
+
hasMore: hasMoreRef.current,
|
|
134
|
+
loading: loadingRef.current,
|
|
135
|
+
error: errorRef.current,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function useConversations(client) {
|
|
139
|
+
const loadingRef = (0, react_1.useRef)(false);
|
|
140
|
+
const getConvs = (0, react_1.useCallback)(() => client.store.getConversations(), [client]);
|
|
141
|
+
const store = (0, react_1.useRef)(externalStore(getConvs)).current;
|
|
142
|
+
(0, react_1.useEffect)(() => {
|
|
143
|
+
const unsubs = [
|
|
144
|
+
client.on("message:sent", () => store.emit()),
|
|
145
|
+
client.on("message:received", () => store.emit()),
|
|
146
|
+
client.on("messages_read", () => store.emit()),
|
|
147
|
+
client.on("messages_delivered", () => store.emit()),
|
|
148
|
+
client.on("conversation:created", () => store.emit()),
|
|
149
|
+
client.on("conversation:updated", () => store.emit()),
|
|
150
|
+
];
|
|
151
|
+
return () => unsubs.forEach((fn) => fn());
|
|
152
|
+
}, [client, store]);
|
|
153
|
+
const conversations = (0, react_1.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
154
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
155
|
+
loadingRef.current = true;
|
|
156
|
+
store.emit();
|
|
157
|
+
try {
|
|
158
|
+
await client.loadInbox();
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
loadingRef.current = false;
|
|
162
|
+
store.emit();
|
|
163
|
+
}
|
|
164
|
+
}, [client, store]);
|
|
165
|
+
return {
|
|
166
|
+
conversations,
|
|
167
|
+
loading: loadingRef.current,
|
|
168
|
+
refresh,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function useContacts(client, context, businessId) {
|
|
172
|
+
const loadingRef = (0, react_1.useRef)(false);
|
|
173
|
+
const params = `${context ?? "buyer"}|${businessId ?? ""}`;
|
|
174
|
+
const getContacts = (0, react_1.useCallback)(() => client.store.getContacts(), [client]);
|
|
175
|
+
const store = (0, react_1.useRef)(externalStore(getContacts)).current;
|
|
176
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
177
|
+
loadingRef.current = true;
|
|
178
|
+
store.emit();
|
|
179
|
+
try {
|
|
180
|
+
await client.loadContacts(context, businessId);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
loadingRef.current = false;
|
|
184
|
+
store.emit();
|
|
185
|
+
}
|
|
186
|
+
}, [client, store, context, businessId]);
|
|
187
|
+
(0, react_1.useEffect)(() => {
|
|
188
|
+
refresh();
|
|
189
|
+
}, [params]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
190
|
+
(0, react_1.useEffect)(() => {
|
|
191
|
+
const unsubs = [
|
|
192
|
+
client.on("message:sent", () => store.emit()),
|
|
193
|
+
client.on("message:received", () => store.emit()),
|
|
194
|
+
client.on("messages_read", () => store.emit()),
|
|
195
|
+
client.on("messages_delivered", () => store.emit()),
|
|
196
|
+
client.on("conversation:created", () => store.emit()),
|
|
197
|
+
client.on("conversation:updated", () => store.emit()),
|
|
198
|
+
];
|
|
199
|
+
return () => unsubs.forEach((fn) => fn());
|
|
200
|
+
}, [client, store]);
|
|
201
|
+
const contacts = (0, react_1.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
202
|
+
return {
|
|
203
|
+
contacts,
|
|
204
|
+
loading: loadingRef.current,
|
|
205
|
+
refresh,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
|