@anakin824/prdg-chat-ui 0.2.0 → 0.2.1
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/dist/index.d.mts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +94 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -169,9 +169,14 @@ type ChatProviderProps = {
|
|
|
169
169
|
natsWsUrl?: string;
|
|
170
170
|
/** Optional NATS auth token (when the server uses token authentication). */
|
|
171
171
|
natsToken?: string;
|
|
172
|
+
/**
|
|
173
|
+
* When true, logs `[prdg-chat]` diagnostics (identity resolution, message `sender_id` vs context `userId`).
|
|
174
|
+
* If omitted, set env `NEXT_PUBLIC_PRDG_CHAT_DEBUG=true` in the host app instead.
|
|
175
|
+
*/
|
|
176
|
+
debug?: boolean;
|
|
172
177
|
children: ReactNode;
|
|
173
178
|
};
|
|
174
|
-
declare function ChatProvider({ apiUrl, token, userId: userIdProp, tenantId: tenantIdProp, conduitlyTenantId: conduitlyTenantIdProp, theme, pollIntervalMs, onUnreadChange: _onUnreadChange, onTokenRefresh, callEnabled, natsWsUrl, natsToken, children, }: ChatProviderProps): react.JSX.Element | null;
|
|
179
|
+
declare function ChatProvider({ apiUrl, token, userId: userIdProp, tenantId: tenantIdProp, conduitlyTenantId: conduitlyTenantIdProp, theme, pollIntervalMs, onUnreadChange: _onUnreadChange, onTokenRefresh, callEnabled, natsWsUrl, natsToken, debug: debugProp, children, }: ChatProviderProps): react.JSX.Element | null;
|
|
175
180
|
|
|
176
181
|
type ListConversationsRes = {
|
|
177
182
|
items: Conversation[];
|
|
@@ -274,6 +279,8 @@ type ChatContextValue = {
|
|
|
274
279
|
* Key: conversationId. Value: array of user IDs (never includes the current user).
|
|
275
280
|
*/
|
|
276
281
|
typingByConversation: Record<string, string[]>;
|
|
282
|
+
/** When true, logs identity + message ownership diagnostics to the console. */
|
|
283
|
+
debug: boolean;
|
|
277
284
|
};
|
|
278
285
|
declare function useChat(): ChatContextValue;
|
|
279
286
|
|
package/dist/index.d.ts
CHANGED
|
@@ -169,9 +169,14 @@ type ChatProviderProps = {
|
|
|
169
169
|
natsWsUrl?: string;
|
|
170
170
|
/** Optional NATS auth token (when the server uses token authentication). */
|
|
171
171
|
natsToken?: string;
|
|
172
|
+
/**
|
|
173
|
+
* When true, logs `[prdg-chat]` diagnostics (identity resolution, message `sender_id` vs context `userId`).
|
|
174
|
+
* If omitted, set env `NEXT_PUBLIC_PRDG_CHAT_DEBUG=true` in the host app instead.
|
|
175
|
+
*/
|
|
176
|
+
debug?: boolean;
|
|
172
177
|
children: ReactNode;
|
|
173
178
|
};
|
|
174
|
-
declare function ChatProvider({ apiUrl, token, userId: userIdProp, tenantId: tenantIdProp, conduitlyTenantId: conduitlyTenantIdProp, theme, pollIntervalMs, onUnreadChange: _onUnreadChange, onTokenRefresh, callEnabled, natsWsUrl, natsToken, children, }: ChatProviderProps): react.JSX.Element | null;
|
|
179
|
+
declare function ChatProvider({ apiUrl, token, userId: userIdProp, tenantId: tenantIdProp, conduitlyTenantId: conduitlyTenantIdProp, theme, pollIntervalMs, onUnreadChange: _onUnreadChange, onTokenRefresh, callEnabled, natsWsUrl, natsToken, debug: debugProp, children, }: ChatProviderProps): react.JSX.Element | null;
|
|
175
180
|
|
|
176
181
|
type ListConversationsRes = {
|
|
177
182
|
items: Conversation[];
|
|
@@ -274,6 +279,8 @@ type ChatContextValue = {
|
|
|
274
279
|
* Key: conversationId. Value: array of user IDs (never includes the current user).
|
|
275
280
|
*/
|
|
276
281
|
typingByConversation: Record<string, string[]>;
|
|
282
|
+
/** When true, logs identity + message ownership diagnostics to the console. */
|
|
283
|
+
debug: boolean;
|
|
277
284
|
};
|
|
278
285
|
declare function useChat(): ChatContextValue;
|
|
279
286
|
|
package/dist/index.js
CHANGED
|
@@ -118,6 +118,25 @@ var ChatAPI = class {
|
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
+
// src/chat/lib/chatDebugLog.ts
|
|
122
|
+
function resolveChatDebug(debugProp) {
|
|
123
|
+
if (debugProp === true) return true;
|
|
124
|
+
if (debugProp === false) return false;
|
|
125
|
+
try {
|
|
126
|
+
return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_PRDG_CHAT_DEBUG === "true";
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function chatDebugLog(debug, ...args) {
|
|
132
|
+
if (!debug) return;
|
|
133
|
+
console.log("[prdg-chat]", ...args);
|
|
134
|
+
}
|
|
135
|
+
function chatDebugWarn(debug, ...args) {
|
|
136
|
+
if (!debug) return;
|
|
137
|
+
console.warn("[prdg-chat]", ...args);
|
|
138
|
+
}
|
|
139
|
+
|
|
121
140
|
// src/chat/lib/queryKeys.ts
|
|
122
141
|
var chatKeys = {
|
|
123
142
|
all: ["chat"],
|
|
@@ -337,8 +356,10 @@ function ChatProvider({
|
|
|
337
356
|
callEnabled = false,
|
|
338
357
|
natsWsUrl,
|
|
339
358
|
natsToken,
|
|
359
|
+
debug: debugProp,
|
|
340
360
|
children
|
|
341
361
|
}) {
|
|
362
|
+
const debug = resolveChatDebug(debugProp);
|
|
342
363
|
const queryClient = react.useMemo(
|
|
343
364
|
() => new reactQuery.QueryClient({
|
|
344
365
|
defaultOptions: {
|
|
@@ -367,15 +388,41 @@ function ChatProvider({
|
|
|
367
388
|
conduitlyTenantIdProp
|
|
368
389
|
);
|
|
369
390
|
const [meError, setMeError] = react.useState(null);
|
|
391
|
+
react.useEffect(() => {
|
|
392
|
+
chatDebugLog(debug, "ChatProvider: session inputs", {
|
|
393
|
+
apiUrl: apiUrl.replace(/\/$/, ""),
|
|
394
|
+
hasToken: Boolean(token?.trim?.()),
|
|
395
|
+
userIdProp: userIdProp ?? null,
|
|
396
|
+
tenantIdProp: tenantIdProp ?? null,
|
|
397
|
+
conduitlyTenantIdProp: conduitlyTenantIdProp ?? null
|
|
398
|
+
});
|
|
399
|
+
}, [debug, apiUrl, token, userIdProp, tenantIdProp, conduitlyTenantIdProp]);
|
|
370
400
|
react.useEffect(() => {
|
|
371
401
|
setMeError(null);
|
|
372
402
|
if (userIdProp) setResolvedUserId(userIdProp);
|
|
373
403
|
if (tenantIdProp) setResolvedTenantId(tenantIdProp);
|
|
374
404
|
if (conduitlyTenantIdProp) setResolvedConduitlyTenantId(conduitlyTenantIdProp);
|
|
375
|
-
if (userIdProp && tenantIdProp)
|
|
405
|
+
if (userIdProp && tenantIdProp) {
|
|
406
|
+
chatDebugLog(debug, "identity: using userId + tenantId props (skipping GET /me)", {
|
|
407
|
+
userId: userIdProp,
|
|
408
|
+
tenantId: tenantIdProp
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
chatDebugLog(debug, "identity: fetching GET /me \u2026", {
|
|
413
|
+
missingUserId: !userIdProp,
|
|
414
|
+
missingTenantId: !tenantIdProp
|
|
415
|
+
});
|
|
376
416
|
let cancelled = false;
|
|
377
417
|
api.getMe().then((me) => {
|
|
378
418
|
if (cancelled) return;
|
|
419
|
+
chatDebugLog(debug, "identity: GET /me ok", {
|
|
420
|
+
id: me.id,
|
|
421
|
+
tenant_id: me.tenant_id,
|
|
422
|
+
display_name: me.display_name,
|
|
423
|
+
ext_user_id: me.ext_user_id ?? null,
|
|
424
|
+
conduitly_tenant_id: me.conduitly_tenant_id ?? null
|
|
425
|
+
});
|
|
379
426
|
if (!userIdProp) setResolvedUserId(me.id);
|
|
380
427
|
if (!tenantIdProp) setResolvedTenantId(me.tenant_id);
|
|
381
428
|
if (!conduitlyTenantIdProp && me.conduitly_tenant_id) {
|
|
@@ -383,11 +430,12 @@ function ChatProvider({
|
|
|
383
430
|
}
|
|
384
431
|
}).catch((err) => {
|
|
385
432
|
if (!cancelled) setMeError(err instanceof Error ? err.message : String(err));
|
|
433
|
+
chatDebugWarn(debug, "identity: GET /me failed", err);
|
|
386
434
|
});
|
|
387
435
|
return () => {
|
|
388
436
|
cancelled = true;
|
|
389
437
|
};
|
|
390
|
-
}, [api, token, userIdProp, tenantIdProp, conduitlyTenantIdProp]);
|
|
438
|
+
}, [api, token, userIdProp, tenantIdProp, conduitlyTenantIdProp, debug]);
|
|
391
439
|
const userId = resolvedUserId ?? "";
|
|
392
440
|
const tenantId = resolvedTenantId ?? "";
|
|
393
441
|
const mergedTheme = react.useMemo(() => ({ ...defaultTheme, ...theme }), [theme]);
|
|
@@ -451,7 +499,8 @@ function ChatProvider({
|
|
|
451
499
|
sendTyping,
|
|
452
500
|
sendTypingStop,
|
|
453
501
|
sendRead,
|
|
454
|
-
typingByConversation
|
|
502
|
+
typingByConversation,
|
|
503
|
+
debug
|
|
455
504
|
}),
|
|
456
505
|
[
|
|
457
506
|
api,
|
|
@@ -465,9 +514,18 @@ function ChatProvider({
|
|
|
465
514
|
sendTyping,
|
|
466
515
|
sendTypingStop,
|
|
467
516
|
sendRead,
|
|
468
|
-
typingByConversation
|
|
517
|
+
typingByConversation,
|
|
518
|
+
debug
|
|
469
519
|
]
|
|
470
520
|
);
|
|
521
|
+
react.useEffect(() => {
|
|
522
|
+
if (!resolvedUserId || !resolvedTenantId) return;
|
|
523
|
+
chatDebugLog(debug, "ChatProvider: context ready", {
|
|
524
|
+
userId: resolvedUserId,
|
|
525
|
+
tenantId: resolvedTenantId,
|
|
526
|
+
natsTenantId
|
|
527
|
+
});
|
|
528
|
+
}, [debug, resolvedUserId, resolvedTenantId, natsTenantId]);
|
|
471
529
|
const layoutStyle = {
|
|
472
530
|
flex: 1,
|
|
473
531
|
minHeight: 0,
|
|
@@ -815,7 +873,7 @@ function escapeReg(s) {
|
|
|
815
873
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
816
874
|
}
|
|
817
875
|
function MessageInput({ conversationId }) {
|
|
818
|
-
const { api, sendTyping, sendTypingStop, userId } = useChat();
|
|
876
|
+
const { api, sendTyping, sendTypingStop, userId, debug } = useChat();
|
|
819
877
|
const qc = reactQuery.useQueryClient();
|
|
820
878
|
const { uploadFile } = useUpload();
|
|
821
879
|
const { recording, start, stop } = useVoiceRecorder();
|
|
@@ -955,6 +1013,17 @@ function MessageInput({ conversationId }) {
|
|
|
955
1013
|
});
|
|
956
1014
|
},
|
|
957
1015
|
onSuccess: (confirmedMessage, _payload, context) => {
|
|
1016
|
+
if (debug && confirmedMessage.sender_id !== userId) {
|
|
1017
|
+
chatDebugWarn(
|
|
1018
|
+
debug,
|
|
1019
|
+
"MessageInput: confirmed sender_id !== context userId (own-bubble alignment will be wrong)",
|
|
1020
|
+
{
|
|
1021
|
+
contextUserId: userId,
|
|
1022
|
+
serverSenderId: confirmedMessage.sender_id,
|
|
1023
|
+
messageId: confirmedMessage.id
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
958
1027
|
qc.setQueryData(chatKeys.messages(conversationId), (old) => {
|
|
959
1028
|
if (!old) return old;
|
|
960
1029
|
const optimisticIdx = old.items.findIndex((m) => m.id === context?.optimisticId);
|
|
@@ -1297,10 +1366,30 @@ function MessageThread({
|
|
|
1297
1366
|
isFetchingOlder,
|
|
1298
1367
|
hasMoreMessages
|
|
1299
1368
|
}) {
|
|
1369
|
+
const { userId, debug } = useChat();
|
|
1300
1370
|
const scrollRef = react.useRef(null);
|
|
1301
1371
|
const bottomRef = react.useRef(null);
|
|
1302
1372
|
const sentinelRef = react.useRef(null);
|
|
1303
1373
|
const ordered = [...messages].reverse();
|
|
1374
|
+
react.useEffect(() => {
|
|
1375
|
+
if (!debug || messages.length === 0) return;
|
|
1376
|
+
const senders = new Set(messages.map((m) => m.sender_id));
|
|
1377
|
+
const ownCount = messages.filter((m) => m.sender_id === userId).length;
|
|
1378
|
+
const mismatchSample = messages.find((m) => m.sender_id !== userId);
|
|
1379
|
+
chatDebugLog(debug, "MessageThread: sender snapshot", {
|
|
1380
|
+
messageCount: messages.length,
|
|
1381
|
+
contextUserId: userId,
|
|
1382
|
+
uniqueSenderIds: [...senders],
|
|
1383
|
+
ownMessageCount: ownCount,
|
|
1384
|
+
contextMatchesAnySender: userId ? senders.has(userId) : false
|
|
1385
|
+
});
|
|
1386
|
+
if (userId && ownCount === 0 && messages.length > 0) {
|
|
1387
|
+
chatDebugWarn(debug, "MessageThread: no messages match context userId (all bubbles show as \u201Cother\u201D)", {
|
|
1388
|
+
contextUserId: userId,
|
|
1389
|
+
sampleSenderId: mismatchSample?.sender_id ?? null
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
}, [debug, messages, userId]);
|
|
1304
1393
|
const seenIdsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
1305
1394
|
const initializedRef = react.useRef(false);
|
|
1306
1395
|
const prevCountRef = react.useRef(0);
|