@droppii-org/chat-sdk 0.6.24 → 0.6.25

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,iBAAiB,EAGlB,MAAM,cAAc,CAAC;AAUtB,eAAO,MAAM,WAAW,0CAQtB,CAAC;AAEH,eAAO,MAAM,cAAc,uBAAgC,CAAC;AAU5D,eAAO,MAAM,YAAY,GAAI,oGAQ1B,iBAAiB,4CAmInB,CAAC"}
1
+ {"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../src/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,sBAAsB,CAAC;AAG9B,OAAO,EACL,eAAe,EACf,iBAAiB,EAGlB,MAAM,cAAc,CAAC;AAUtB,eAAO,MAAM,WAAW,0CAQtB,CAAC;AAEH,eAAO,MAAM,cAAc,uBAAgC,CAAC;AAU5D,eAAO,MAAM,YAAY,GAAI,oGAQ1B,iBAAiB,4CA8InB,CAAC"}
@@ -6,7 +6,7 @@ import { ConnectStatus, SyncStatus, } from "../types/chat";
6
6
  import { DChatSDK } from "../constants/sdk";
7
7
  import MainLayout from "../layout";
8
8
  import useAuthStore from "../store/auth";
9
- import { setUrlStateEnabled } from "../store/conversation";
9
+ import { setUrlNavigate, setUrlStateEnabled } from "../store/conversation";
10
10
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
11
11
  import { ConfigProvider } from "antd";
12
12
  const queryClient = new QueryClient();
@@ -35,6 +35,16 @@ export const ChatProvider = ({ children, config, renderCustomMessage, onNavigate
35
35
  useEffect(() => {
36
36
  setUrlStateEnabled(useUrlState);
37
37
  }, [useUrlState]);
38
+ // Lets clearSelectedConversation route URL cleanup through the host's
39
+ // router instead of a raw history.replaceState.
40
+ // Gated on useUrlState like useChatNavigate, even though the sole current
41
+ // consumer (clearSelectedConversation) already checks urlStateEnabled
42
+ // itself — keeps this registration from becoming a footgun if a future
43
+ // bubble-mode-unaware consumer is added.
44
+ useEffect(() => {
45
+ setUrlNavigate(useUrlState ? (onNavigate !== null && onNavigate !== void 0 ? onNavigate : null) : null);
46
+ return () => setUrlNavigate(null);
47
+ }, [onNavigate, useUrlState]);
38
48
  const themeStyle = {
39
49
  "--dc-color-primary": (_a = theme === null || theme === void 0 ? void 0 : theme.primary) !== null && _a !== void 0 ? _a : DEFAULT_THEME["--dc-color-primary"],
40
50
  "--dc-color-primary-hover": (_b = theme === null || theme === void 0 ? void 0 : theme.primaryHover) !== null && _b !== void 0 ? _b : DEFAULT_THEME["--dc-color-primary-hover"],
@@ -1 +1 @@
1
- {"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAe,MAAM,8BAA8B,CAAC;AAU7E,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2BD,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAWjC,CAAC;AAwCF,eAAO,MAAM,sBAAsB,GAAI,wDAMpC,4BAA4B;;;CA4H9B,CAAC"}
1
+ {"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAe,MAAM,8BAA8B,CAAC;AAU7E,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2BD,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAWjC,CAAC;AAkDF,eAAO,MAAM,sBAAsB,GAAI,wDAMpC,4BAA4B;;;CA4H9B,CAAC"}
@@ -32,21 +32,37 @@ export const fetchConversationByBotId = async (botId) => {
32
32
  const fetchConversationByTarget = ({ conversationId, userId, groupId, botId, }) => {
33
33
  if (conversationId)
34
34
  return fetchByConversationId(conversationId);
35
- if (userId)
36
- return fetchBySource(userId, SessionType.Single);
35
+ // groupId before userId: a group-message notification link carries both
36
+ // (the group, plus the sender's userId as secondary info) — resolving
37
+ // userId first would wrongly open a 1:1 thread with the sender instead
38
+ // of the group.
37
39
  if (groupId)
38
40
  return fetchBySource(groupId, SessionType.Group);
39
41
  if (botId)
40
42
  return fetchConversationByBotId(botId);
43
+ if (userId)
44
+ return fetchBySource(userId, SessionType.Single);
41
45
  return Promise.resolve(null);
42
46
  };
47
+ // Mirrors fetchConversationByTarget's conversationId → groupId → botId →
48
+ // userId precedence exactly — matching on any present field independently
49
+ // (the old OR-chain) let a stale field win over the one that's actually
50
+ // authoritative. Concretely: viewing a 1:1 with sender X, then a group
51
+ // notification for the same sender arrives ({groupId, userId: X}) — matching
52
+ // on userId alone would treat the already-open 1:1 as satisfying the group
53
+ // target and skip resolving the group entirely.
43
54
  const matchesTarget = (conversation, { conversationId, userId, groupId, botId }) => {
44
55
  if (!conversation)
45
56
  return false;
46
- return ((!!conversationId && conversation.conversationID === conversationId) ||
47
- (!!userId && conversation.userID === userId) ||
48
- (!!groupId && conversation.groupID === groupId) ||
49
- (!!botId && resolveConversationBotId(conversation) === botId));
57
+ if (conversationId)
58
+ return conversation.conversationID === conversationId;
59
+ if (groupId)
60
+ return conversation.groupID === groupId;
61
+ if (botId)
62
+ return resolveConversationBotId(conversation) === botId;
63
+ if (userId)
64
+ return conversation.userID === userId;
65
+ return false;
50
66
  };
51
67
  const targetKey = ({ conversationId, userId, groupId, botId, messageId, }) => `${conversationId !== null && conversationId !== void 0 ? conversationId : ""}|${userId !== null && userId !== void 0 ? userId : ""}|${groupId !== null && groupId !== void 0 ? groupId : ""}|${botId !== null && botId !== void 0 ? botId : ""}|${messageId !== null && messageId !== void 0 ? messageId : ""}`;
52
68
  export const useResolveConversation = ({ conversationId, userId, groupId, botId, messageId, }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,6FAA6F;IAC7F,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E;AA2ND,QAAA,MAAM,sBAAsB,GAAI,OAAO,2BAA2B,4CAQjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-inbox/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/D,MAAM,WAAW,2BAA2B;IAC1C,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,6FAA6F;IAC7F,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,yEAAyE;IACzE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3E;AAkOD,QAAA,MAAM,sBAAsB,GAAI,OAAO,2BAA2B,4CAQjE,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -80,20 +80,24 @@ const useInboxConversationList = (chatCategory) => {
80
80
  const DChatConversationInboxBody = ({ onSelectThread, className, moreOptions, showSearch = true, headerTitle, headerRightSlot, onCopyMessageLink, target, conversationList, }) => {
81
81
  const { t } = useTranslation("conversation-inbox");
82
82
  const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
83
+ const removedFromGroupModalOpen = useConversationStore((state) => state.removedFromGroupModalOpen);
83
84
  const [isSearchOpen, { setTrue: openSearch, setFalse: closeSearch }] = useBoolean(false);
84
85
  const { threadId, userId, groupId, botId, messageId } = target;
85
86
  // Any of the four targets opens the detail screen — useResolveConversation
86
87
  // turns userId/groupId/botId into a thread (creating the bot one when
87
88
  // missing), so entry points that only know the peer still work.
88
89
  // threadId alone must also match selectedConversationId: clearSelectedConversation
89
- // strips threadId from the URL via history.replaceState, which Next's Pages
90
- // Router compat shim for useSearchParams doesn't react to (only router.push/
91
- // replace do) — without this check, a stale threadId here would keep
92
- // rendering the detail screen against an already-cleared store forever.
93
- const hasConversationTarget = !!((threadId && threadId === selectedConversationId) ||
94
- userId ||
95
- groupId ||
96
- botId);
90
+ // strips threadId (and groupId/userId/botId) from the URL through the
91
+ // host's router (setUrlNavigate) when one is registered, so useSearchParams
92
+ // picks up the change on its own — but the
93
+ // modal-open check still covers the brief render between the reject firing
94
+ // and that router navigation completing, so the list shows immediately
95
+ // instead of flashing empty behind the modal.
96
+ const hasConversationTarget = !removedFromGroupModalOpen &&
97
+ !!((threadId && threadId === selectedConversationId) ||
98
+ userId ||
99
+ groupId ||
100
+ botId);
97
101
  if (hasConversationTarget) {
98
102
  return (_jsx(DChatConversationDetail, { className: className, moreOptions: moreOptions, conversationId: threadId !== null && threadId !== void 0 ? threadId : undefined, userId: userId, groupId: groupId, botId: botId, onCopyMessageLink: onCopyMessageLink, messageId: messageId }));
99
103
  }
@@ -1,5 +1,6 @@
1
1
  import { ConversationStore } from "./type";
2
2
  export declare const setUrlStateEnabled: (enabled: boolean) => void;
3
+ export declare const setUrlNavigate: (fn: ((url: string) => void) | null) => void;
3
4
  declare const useConversationStore: import("zustand").UseBoundStore<import("zustand").StoreApi<ConversationStore>>;
4
5
  export default useConversationStore;
5
6
  //# sourceMappingURL=conversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAEF,QAAA,MAAM,oBAAoB,gFAuYvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAUF,eAAO,MAAM,cAAc,GAAI,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,SAEhE,CAAC;AAEF,QAAA,MAAM,oBAAoB,gFAsZvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
@@ -13,6 +13,17 @@ let urlStateEnabled = true;
13
13
  export const setUrlStateEnabled = (enabled) => {
14
14
  urlStateEnabled = enabled;
15
15
  };
16
+ // Module-level (not store state): ChatProvider syncs this from its onNavigate
17
+ // prop, same reason as urlStateEnabled above. clearSelectedConversation uses
18
+ // this instead of window.history.replaceState directly — Next's Pages Router
19
+ // compat shim for useSearchParams only re-reads the URL after a router
20
+ // navigation, never after a raw history.replaceState, so a caller reading
21
+ // groupId/userId/botId via useSearchParams would keep seeing the stale,
22
+ // just-cleared value forever.
23
+ let navigate = null;
24
+ export const setUrlNavigate = (fn) => {
25
+ navigate = fn;
26
+ };
16
27
  const useConversationStore = create((set, get) => ({
17
28
  conversationData: null,
18
29
  setConversationData: (data, dataSearchClientMsgID, options) => {
@@ -258,19 +269,37 @@ const useConversationStore = create((set, get) => ({
258
269
  clearSelectedConversation: () => {
259
270
  // DeskConversationList's threadId-driven effect re-selects the same
260
271
  // conversation on its next run otherwise (conversationList changes
261
- // trigger it) — clobbering this reset right back. No onNavigate/router
262
- // dependency by design: this store has no access to one.
272
+ // trigger it) — clobbering this reset right back.
263
273
  if (urlStateEnabled && typeof window !== "undefined") {
264
274
  const params = new URLSearchParams(window.location.search);
265
- // `conversationId` is an accepted alias for `threadId` — leaving it
266
- // behind would let the next effect run re-select what we just cleared.
267
- const hadThreadParam = params.has("threadId") || params.has("conversationId");
268
- if (hadThreadParam) {
269
- params.delete("threadId");
270
- params.delete("conversationId");
275
+ // `conversationId` is an accepted alias for `threadId`; groupId/botId/
276
+ // userId are the BIZ inbox's notification/deep-link entry params —
277
+ // leaving any of these behind would let the next effect run (or a
278
+ // URL-reading hasConversationTarget check) re-select the target we
279
+ // just rejected. Listed in the same conversationId -> groupId -> userId
280
+ // -> botId precedence useResolveConversation resolves them by.
281
+ const staleParams = [
282
+ "threadId",
283
+ "conversationId",
284
+ "groupId",
285
+ "botId",
286
+ "userId",
287
+ ];
288
+ const hadStaleParam = staleParams.some((key) => params.has(key));
289
+ if (hadStaleParam) {
290
+ staleParams.forEach((key) => params.delete(key));
271
291
  const query = params.toString();
272
292
  const url = `${window.location.pathname}${query ? `?${query}` : ""}`;
273
- window.history.replaceState(null, "", url);
293
+ // Prefer the host's router (set via setUrlNavigate) so Next's
294
+ // useSearchParams actually re-reads the cleared params on the next
295
+ // render — a raw history.replaceState leaves useSearchParams stale
296
+ // until an unrelated router navigation happens to occur. Falls back
297
+ // to history.replaceState when no navigate fn is registered (e.g.
298
+ // CRM never set one — matches this function's prior behavior there).
299
+ if (navigate)
300
+ navigate(url);
301
+ else
302
+ window.history.replaceState(null, "", url);
274
303
  }
275
304
  }
276
305
  // Deselecting is normal navigation, not a reason to drop the groupID
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.6.24",
3
+ "version": "0.6.25",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",