@droppii-org/chat-sdk 0.6.23 → 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":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAmB/D,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,QAAA,MAAM,uBAAuB,GAAI,4EAM9B,4BAA4B,4CAorB9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAmB/D,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,QAAA,MAAM,uBAAuB,GAAI,4EAM9B,4BAA4B,4CAurB9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -97,11 +97,15 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
97
97
  const [dismissedTriggerStart, setDismissedTriggerStart] = useState(null);
98
98
  // 1v1 threads have no groupID — mentions are a group-only concept
99
99
  // (matches CRM's MentionMenu, which already guards on the same field).
100
+ // Bot threads don't support tagging at all — not just CS staff filtering
101
+ // (DROPPII-30799 follow-up): the whole trigger is suppressed, same as
102
+ // groupless 1v1 threads.
100
103
  const mentionTrigger = !(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) ||
104
+ isBotPeer(conversationData) ||
101
105
  (rawMentionTrigger === null || rawMentionTrigger === void 0 ? void 0 : rawMentionTrigger.triggerStart) === dismissedTriggerStart
102
106
  ? null
103
107
  : rawMentionTrigger;
104
- const rawMentionOptions = useMentionOptions(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID, (_a = mentionTrigger === null || mentionTrigger === void 0 ? void 0 : mentionTrigger.query) !== null && _a !== void 0 ? _a : "", conversationData ? isBotPeer(conversationData) : false);
108
+ const rawMentionOptions = useMentionOptions(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID, (_a = mentionTrigger === null || mentionTrigger === void 0 ? void 0 : mentionTrigger.query) !== null && _a !== void 0 ? _a : "");
105
109
  const isMentionMenuOpen = mentionTrigger !== null;
106
110
  const [selectedMentionIndex, setSelectedMentionIndex] = useState(0);
107
111
  const { tokens: mentionTokens, addToken, onValueChange, resetTokens } = useMentionTokens();
@@ -1 +1 @@
1
- {"version":3,"file":"MentionMenu.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/MentionMenu.tsx"],"names":[],"mappings":"AA8BA,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AAWjE,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAG5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,WAAW,GAAI,0BAA0B,gBAAgB,mDAyI9D,CAAC;AA+CF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MentionMenu.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/MentionMenu.tsx"],"names":[],"mappings":"AA8BA,eAAO,MAAM,qBAAqB,8BAA8B,CAAC;AAWjE,UAAU,gBAAgB;IACxB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAE5B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,QAAA,MAAM,WAAW,GAAI,0BAA0B,gBAAgB,mDA4I9D,CAAC;AA+CF,eAAe,WAAW,CAAC"}
@@ -41,7 +41,7 @@ const MentionMenu = ({ groupID, isBotThread }) => {
41
41
  const checkForMentionMatch = useBasicTypeaheadTriggerMatch("@", {
42
42
  minLength: 0,
43
43
  });
44
- const options = useMentionOptions(groupID, query !== null && query !== void 0 ? query : "", isBotThread);
44
+ const options = useMentionOptions(groupID, query !== null && query !== void 0 ? query : "");
45
45
  // Re-evaluated per query change (menu re-opens/re-queries on every "@"),
46
46
  // so this stays in sync with mentions added/removed since the last count.
47
47
  const mentionedUserIDs = useMemo(() => editor.getEditorState().read(() => $getMentionedUserIDs($getRoot())), [editor, query]);
@@ -71,7 +71,10 @@ const MentionMenu = ({ groupID, isBotThread }) => {
71
71
  });
72
72
  closeMenu();
73
73
  }, [editor]);
74
- if (!groupID)
74
+ // Bot threads don't support tagging at all, not just CS-staff filtering
75
+ // (DROPPII-30799 follow-up) — the whole trigger is suppressed, same as
76
+ // groupless 1v1 threads.
77
+ if (!groupID || isBotThread)
75
78
  return null;
76
79
  return (_jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQuery, onSelectOption: onSelectOption, onOpen: () => setIsMentionMenuOpen(true), onClose: () => setIsMentionMenuOpen(false), triggerFn: checkForMentionMatch, options: typeaheadOptions, menuRenderFn: (anchorElementRef, { selectedIndex, setHighlightedIndex, selectOptionAndCleanUp }) => {
77
80
  if (!anchorElementRef.current)
@@ -5,5 +5,5 @@ export interface MentionOption {
5
5
  isAtAll: boolean;
6
6
  }
7
7
  export declare const ALL_MEMBERS_USER_ID = "__all__";
8
- export declare const useMentionOptions: (groupID: string | undefined, query: string, excludeStaff?: boolean) => MentionOption[];
8
+ export declare const useMentionOptions: (groupID: string | undefined, query: string) => MentionOption[];
9
9
  //# sourceMappingURL=useMentionOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,YAAY,CAAC;AA0B7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,EAIb,sBAAoB,KACnB,aAAa,EA+Df,CAAC"}
1
+ {"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAe7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EAyCf,CAAC"}
@@ -1,10 +1,7 @@
1
- import { useEffect, useMemo } from "react";
1
+ import { useMemo } from "react";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import { useGroupMembers } from "../../../../hooks/group/useGroupMembers";
4
4
  import useAuthStore from "../../../../store/auth";
5
- import useUsersInfoStore from "../../../../store/usersInfo";
6
- import { DChatSDK } from "../../../../constants/sdk";
7
- import { AccountType } from "../../../../types/chat";
8
5
  export const ALL_MEMBERS_USER_ID = "__all__";
9
6
  // Prefix matches rank above mid-string matches, then alphabetical — matches
10
7
  // the "closest match first" ranking spec'd for this feature (not just a
@@ -15,52 +12,20 @@ const matchRank = (displayName, keyword) => {
15
12
  return 0;
16
13
  return 1;
17
14
  };
18
- // Mirrors useCurrentUserAccountType's parsing of the same ex-encoded field.
19
- // Takes the account-level `ex` (PublicUserItem, from getUsersInfo) — NOT
20
- // GroupMemberItem.ex, which is a separate per-membership field that never
21
- // carries accountType.
22
- const isStaffMember = (ex) => {
23
- var _a, _b, _c;
24
- try {
25
- return (((_c = (_b = (_a = JSON.parse(ex || "{}")) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.accountType) === AccountType.Staff);
26
- }
27
- catch (_d) {
28
- return false;
29
- }
30
- };
31
- export const useMentionOptions = (groupID, query,
32
- // Bot threads shouldn't surface CS/staff accounts as tag targets — they
33
- // aren't part of the customer-facing conversation. Real member-managed
34
- // groups have no such restriction (DROPPII-30799).
35
- excludeStaff = false) => {
15
+ // Bot threads don't reach this hook at all — both mention triggers
16
+ // (ConversationDetailInput, MentionMenu) suppress the trigger entirely for
17
+ // bot peers (DROPPII-30799 follow-up), so there's no staff-filtering concern
18
+ // left to handle here.
19
+ export const useMentionOptions = (groupID, query) => {
36
20
  const { t } = useTranslation();
37
21
  const { members } = useGroupMembers(groupID);
38
22
  const currentUserID = useAuthStore((state) => state.userID);
39
- const usersInfo = useUsersInfoStore((state) => state.usersInfo);
40
- // Only fetch when actually needed: bot threads (excludeStaff) require the
41
- // account-level accountType, which GroupMemberItem.ex doesn't carry.
42
- useEffect(() => {
43
- if (!excludeStaff || members.length === 0)
44
- return;
45
- const { usersInfo: cache, upsertUsers } = useUsersInfoStore.getState();
46
- const missingIds = members
47
- .map((member) => member.userID)
48
- .filter((userID) => !cache[userID]);
49
- if (missingIds.length === 0)
50
- return;
51
- DChatSDK.getUsersInfo(missingIds)
52
- .then(({ data }) => upsertUsers(data))
53
- .catch((error) => {
54
- console.error("Error fetching mention candidates' user info:", error);
55
- });
56
- }, [excludeStaff, members]);
57
23
  return useMemo(() => {
58
24
  const keyword = query.trim().toLowerCase();
59
25
  // Self-tagging isn't a useful mention target — the sender already
60
26
  // knows who they are — so the current user is filtered out entirely.
61
27
  const memberOptions = members
62
28
  .filter((member) => member.userID !== currentUserID)
63
- .filter((member) => { var _a; return !excludeStaff || !isStaffMember((_a = usersInfo[member.userID]) === null || _a === void 0 ? void 0 : _a.ex); })
64
29
  .map((member) => ({
65
30
  userID: member.userID,
66
31
  displayName: member.nickname || member.userID,
@@ -83,5 +48,5 @@ excludeStaff = false) => {
83
48
  };
84
49
  const matchesAll = !keyword || allOption.displayName.toLowerCase().includes(keyword);
85
50
  return matchesAll ? [allOption, ...memberOptions] : memberOptions;
86
- }, [members, query, t, currentUserID, excludeStaff, usersInfo]);
51
+ }, [members, query, t, currentUserID]);
87
52
  };
@@ -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.23",
3
+ "version": "0.6.25",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",