@droppii-org/chat-sdk 0.8.3 → 0.8.5

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.
Files changed (37) hide show
  1. package/dist/components/canned-response/CannedResponseBody.js +1 -1
  2. package/dist/components/common/ShadowSafeInfiniteScroll.d.ts +15 -0
  3. package/dist/components/common/ShadowSafeInfiniteScroll.d.ts.map +1 -0
  4. package/dist/components/common/ShadowSafeInfiniteScroll.js +45 -0
  5. package/dist/components/conversation/DeskConversationList.js +1 -1
  6. package/dist/components/conversation-detail/ConversationMessageList.js +2 -2
  7. package/dist/components/conversation-detail/thread-search/ThreadSearchContent.js +1 -1
  8. package/dist/components/conversation-inbox/ConversationInboxList.js +1 -1
  9. package/dist/components/conversation-inbox/PublicGroupList.js +1 -1
  10. package/dist/components/conversation-inbox/contact-search/ContactSearchMessagesTab.js +1 -1
  11. package/dist/components/conversation-inbox/contact-search/ContactSearchPanel.js +1 -1
  12. package/dist/components/media-collection/FileCollection.js +1 -1
  13. package/dist/components/media-collection/ImageCollection.js +1 -1
  14. package/dist/components/media-collection/LinkCollection.js +2 -2
  15. package/dist/components/media-collection/VideoCollection.js +1 -1
  16. package/dist/components/media-resources/ResourceScrollContainer.js +1 -1
  17. package/dist/components/message/MessageInfiniteScroll.js +1 -1
  18. package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
  19. package/dist/components/message/footer/EnterHandler.js +14 -1
  20. package/dist/components/message/footer/ToolbarPlugin.d.ts.map +1 -1
  21. package/dist/components/message/footer/ToolbarPlugin.js +9 -1
  22. package/dist/components/message/footer/resetEditorAfterSend.d.ts.map +1 -1
  23. package/dist/components/message/footer/resetEditorAfterSend.js +7 -11
  24. package/dist/components/message/item/index.js +2 -2
  25. package/dist/components/search-conversation/SearchConversationAsGroups.js +1 -1
  26. package/dist/components/search-conversation/SearchConversationAsMessages.js +1 -1
  27. package/dist/components/search-conversation/SearchConversationAsUsers.js +1 -1
  28. package/dist/components/search-conversation/SearchMessageOnCurrentConversation.js +1 -1
  29. package/dist/hooks/message/useMessage.d.ts +1 -1
  30. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  31. package/dist/hooks/message/useMessage.js +6 -8
  32. package/dist/hooks/message/useMessageListScroll.d.ts.map +1 -1
  33. package/dist/hooks/message/useMessageListScroll.js +10 -3
  34. package/dist/hooks/session/useSyncGroupStateAfterJoin.d.ts.map +1 -1
  35. package/dist/hooks/session/useSyncGroupStateAfterJoin.js +15 -2
  36. package/dist/locales/vi/common.json +1 -0
  37. package/package.json +1 -1
@@ -7,7 +7,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import { CannedResponseVisibleScope } from "../../types/chat";
8
8
  import TeamItem from "./team/TeamItem";
9
9
  import { useFetchCannedResponse } from "../../hooks/canned-response/useFetchCannedResponse";
10
- import InfiniteScroll from "react-infinite-scroll-component";
10
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
11
11
  import { Icon } from "../../components/icon";
12
12
  import { sanitizeHtml } from "../../utils/common";
13
13
  import { useDebounce } from "ahooks";
@@ -0,0 +1,15 @@
1
+ import { ComponentProps } from "react";
2
+ import InfiniteScroll from "react-infinite-scroll-component";
3
+ type InfiniteScrollProps = ComponentProps<typeof InfiniteScroll>;
4
+ /**
5
+ * Drop-in replacement for `react-infinite-scroll-component` that resolves a
6
+ * string `scrollableTarget` inside the tree the list is mounted in.
7
+ *
8
+ * The library looks the id up with `document.getElementById`, which cannot see
9
+ * into a shadow root (Mall renders the chat in one), so the scroll listener
10
+ * never attached and page 2 was never requested. Here the id is resolved
11
+ * through the probe's own root node and the element is handed to the library.
12
+ */
13
+ declare const ShadowSafeInfiniteScroll: ({ scrollableTarget, children, ...rest }: InfiniteScrollProps) => import("react/jsx-runtime").JSX.Element;
14
+ export default ShadowSafeInfiniteScroll;
15
+ //# sourceMappingURL=ShadowSafeInfiniteScroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShadowSafeInfiniteScroll.d.ts","sourceRoot":"","sources":["../../../src/components/common/ShadowSafeInfiniteScroll.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAyB,MAAM,OAAO,CAAC;AAC9D,OAAO,cAAc,MAAM,iCAAiC,CAAC;AAE7D,KAAK,mBAAmB,GAAG,cAAc,CAAC,OAAO,cAAc,CAAC,CAAC;AAEjE;;;;;;;;GAQG;AACH,QAAA,MAAM,wBAAwB,GAAI,yCAI/B,mBAAmB,4CA2CrB,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
14
+ import { useCallback, useState } from "react";
15
+ import InfiniteScroll from "react-infinite-scroll-component";
16
+ /**
17
+ * Drop-in replacement for `react-infinite-scroll-component` that resolves a
18
+ * string `scrollableTarget` inside the tree the list is mounted in.
19
+ *
20
+ * The library looks the id up with `document.getElementById`, which cannot see
21
+ * into a shadow root (Mall renders the chat in one), so the scroll listener
22
+ * never attached and page 2 was never requested. Here the id is resolved
23
+ * through the probe's own root node and the element is handed to the library.
24
+ */
25
+ const ShadowSafeInfiniteScroll = (_a) => {
26
+ var { scrollableTarget, children } = _a, rest = __rest(_a, ["scrollableTarget", "children"]);
27
+ const targetId = typeof scrollableTarget === "string" ? scrollableTarget : null;
28
+ const [resolved, setResolved] = useState(null);
29
+ const probeRef = useCallback((node) => {
30
+ if (!node || !targetId)
31
+ return;
32
+ const root = node.getRootNode();
33
+ const element = typeof root.getElementById === "function"
34
+ ? root.getElementById(targetId)
35
+ : null;
36
+ setResolved({ id: targetId, target: element !== null && element !== void 0 ? element : targetId });
37
+ }, [targetId]);
38
+ if (!targetId) {
39
+ return (_jsx(InfiniteScroll, Object.assign({}, rest, { scrollableTarget: scrollableTarget, children: children })));
40
+ }
41
+ return (_jsxs(_Fragment, { children: [_jsx("span", { ref: probeRef, hidden: true, "aria-hidden": "true" }), (resolved === null || resolved === void 0 ? void 0 : resolved.id) === targetId && (
42
+ // The library types the prop as ReactNode but accepts an element.
43
+ _jsx(InfiniteScroll, Object.assign({}, rest, { scrollableTarget: resolved.target, children: children })))] }));
44
+ };
45
+ export default ShadowSafeInfiniteScroll;
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next";
8
8
  import { useBoolean, useDebounce } from "ahooks";
9
9
  import SearchConversation from "../../components/search-conversation";
10
10
  import useSessionStore from "../../store/session";
11
- import InfiniteScroll from "react-infinite-scroll-component";
11
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
12
12
  import ConversationBySessionItem from "./ConversationBySessionItem";
13
13
  import ConversationByGroupItem from "./ConversationByGroupItem";
14
14
  import { useGetSessionList } from "../../hooks/session/useGetSessionList";
@@ -43,8 +43,8 @@ const ConversationMessageList = ({ bottomSlot, onCopyMessageLink, onSearchMessag
43
43
  const canReply = usePeerLinkState(conversation) !== "unlinked";
44
44
  const viewerUserID = (user === null || user === void 0 ? void 0 : user.userID) || authUserID;
45
45
  const isAgentView = isSupportSessionAgentView(Object.assign(Object.assign({}, conversation), { conversationID: conversationId }), viewerUserID);
46
- // App history strips the assignment log and session dividers, which an
47
- // agent handling someone else's session needs — load the full history.
46
+ // An agent handling someone else's session keeps loading the full history;
47
+ // the assignment log and session dividers in it are hidden at render.
48
48
  const useAppHistory = !isAgentView;
49
49
  const messageController = useMessage(conversationId, searchClientMsgID, true, useAppHistory, onSearchMessageRevoked);
50
50
  useViewOnlyGroupRefresh({
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Input, Spin, message as toastMessage } from "antd";
5
5
  import { useDebounce } from "ahooks";
6
- import InfiniteScroll from "react-infinite-scroll-component";
6
+ import InfiniteScroll from "../../common/ShadowSafeInfiniteScroll";
7
7
  import { useTranslation } from "react-i18next";
8
8
  import { MessageType, } from "@droppii-org/wasm-client-sdk";
9
9
  import { Icon } from "../../../components/icon";
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { Empty } from "antd";
5
- import InfiniteScroll from "react-infinite-scroll-component";
5
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
6
6
  import useConversationStore from "../../store/conversation";
7
7
  import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
8
8
  import { Icon } from "../../components/icon";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Spin } from "antd";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import { useChatContext } from "../../context/ChatContext";
7
7
  import { useChatNavigate } from "../../hooks/conversation/useChatNavigate";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Spin } from "antd";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../../common/ShadowSafeInfiniteScroll";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import ContactSearchMessageResultItem from "./ContactSearchMessageResultItem";
7
7
  import ContactSearchStateMessage from "./ContactSearchStateMessage";
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useEffect, useState } from "react";
4
4
  import { Drawer, Input, Spin, message as toastMessage, } from "antd";
5
5
  import { useDebounce } from "ahooks";
6
- import InfiniteScroll from "react-infinite-scroll-component";
6
+ import InfiniteScroll from "../../common/ShadowSafeInfiniteScroll";
7
7
  import { useTranslation } from "react-i18next";
8
8
  import { MessageType } from "@droppii-org/wasm-client-sdk";
9
9
  import { Icon } from "../../../components/icon";
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MessageType } from "@droppii-org/wasm-client-sdk";
3
3
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import { Empty, Spin } from "antd";
6
6
  import { useCallback } from "react";
7
7
  import dayjs from "dayjs";
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MessageType } from "@droppii-org/wasm-client-sdk";
3
3
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import { Empty, Image, Spin } from "antd";
6
6
  import { useCallback } from "react";
7
7
  import dayjs from "dayjs";
@@ -6,7 +6,7 @@ import { useSearchMessage } from "../../hooks/search/useSearchMessage";
6
6
  import { useCallback } from "react";
7
7
  import dayjs from "dayjs";
8
8
  import { LIBRARY_DATE_HEADER_FORMAT } from "../../constants/dateFormat";
9
- import InfiniteScroll from "react-infinite-scroll-component";
9
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
10
10
  import { MessageType, SessionType } from "@droppii-org/wasm-client-sdk";
11
11
  import { Icon } from "../../components/icon";
12
12
  import { DChatSDK } from "../../constants/sdk";
@@ -72,7 +72,7 @@ const LinkCollection = ({ onClose }) => {
72
72
  }) })] }, date));
73
73
  }, []);
74
74
  if (dataFlatten.length === 0 && !isLoading)
75
- return _jsx(Empty, { description: t("no_media_files") });
75
+ return _jsx(Empty, { description: t("no_thread_links") });
76
76
  if (isLoading)
77
77
  return (_jsx("div", { className: "flex items-center justify-center", children: _jsx(Spin, {}) }));
78
78
  return (_jsx("div", { id: "scrollableFileDiv", "data-cp-name": "LinkCollection", className: "h-full overflow-auto", children: _jsx(InfiniteScroll, { dataLength: Object.keys(groupedData).length, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableFileDiv", children: Object.entries(groupedData).map(([date, items]) => renderItem(date, items)) }) }));
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MessageType } from "@droppii-org/wasm-client-sdk";
3
3
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import { Empty, Image, Spin } from "antd";
6
6
  import { useState } from "react";
7
7
  import dayjs from "dayjs";
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import { Button, Spin } from "antd";
6
6
  import { useTranslation } from "react-i18next";
7
7
  import ResourceEmptyState from "./ResourceEmptyState";
@@ -2,6 +2,6 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Spin } from "antd";
4
4
  import clsx from "clsx";
5
- import InfiniteScroll from "react-infinite-scroll-component";
5
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
6
6
  const MessageInfiniteScroll = ({ scrollRef, scrollableTargetId, loadState, moreOldLoading = false, onLoadMoreOld, onScroll, children, empty, containerClassName, containerProps, }) => (_jsx("div", Object.assign({ id: scrollableTargetId, ref: scrollRef, className: clsx("flex h-full flex-col-reverse overflow-x-hidden overflow-y-auto pb-3", containerClassName), "data-cp-name": "MessageInfiniteScroll" }, containerProps, { children: empty && loadState.messageList.length === 0 ? (empty) : (_jsx(InfiniteScroll, { dataLength: loadState.messageList.length, next: onLoadMoreOld, className: "flex w-full min-w-0 flex-col-reverse", inverse: true, hasMore: loadState.hasMoreOld, loader: moreOldLoading ? (_jsx("div", { className: "flex w-full items-center justify-center py-2", children: _jsx(Spin, { size: "small" }) })) : (_jsx("span", { className: "sr-only" })), scrollableTarget: scrollableTargetId, onScroll: (e) => onScroll(e), children: children })) })));
7
7
  export default MessageInfiniteScroll;
@@ -1 +1 @@
1
- {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,YAAY,SAkHnC"}
1
+ {"version":3,"file":"EnterHandler.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/EnterHandler.tsx"],"names":[],"mappings":"AA+BA,MAAM,CAAC,OAAO,UAAU,YAAY,SAyHnC"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
3
- import { KEY_ENTER_COMMAND, COMMAND_PRIORITY_NORMAL, $createParagraphNode, $getRoot, } from "lexical";
3
+ import { KEY_ENTER_COMMAND, COMMAND_PRIORITY_NORMAL, $createParagraphNode, $getRoot, $getSelection, $isRangeSelection, } from "lexical";
4
+ import { $isListNode } from "@lexical/list";
4
5
  import { clearComposerHistoryAfterUpdate } from "./clearComposerHistory";
5
6
  import { resetEditorAfterSend, resetEditorIfUnchanged } from "./resetEditorAfterSend";
6
7
  import { useEffect, useRef } from "react";
@@ -11,6 +12,12 @@ import emitter, { emit } from "../../../utils/events";
11
12
  import { extractMentions } from "./mention/extractMentions";
12
13
  import useAuthStore from "../../../store/auth";
13
14
  import { LAYOUT_MEDIA } from "../../../constants/layout";
15
+ const isCaretInList = (editor) => editor.getEditorState().read(() => {
16
+ const selection = $getSelection();
17
+ if (!$isRangeSelection(selection))
18
+ return false;
19
+ return $isListNode(selection.anchor.getNode().getTopLevelElement());
20
+ });
14
21
  export default function EnterHandler() {
15
22
  const [editor] = useLexicalComposerContext();
16
23
  const { onSendMessage, isInternal, listUploadFiles, isOpenCanned, activeCannedIndex, isMentionMenuOpen, } = useMessageFooterContext();
@@ -37,6 +44,12 @@ export default function EnterHandler() {
37
44
  // A null event (IME composition end) has nothing to send or cancel on desktop.
38
45
  if (isCrm && !event)
39
46
  return true;
47
+ // Enter inside a list item continues (or, on an empty item, leaves) the
48
+ // list through Lexical's own handler; Ctrl/Cmd+Enter or the Send button
49
+ // sends from there.
50
+ if (!isOpenCanned && event && !event.ctrlKey && !event.metaKey && isCaretInList(editor)) {
51
+ return false;
52
+ }
40
53
  event.preventDefault();
41
54
  if (isOpenCanned) {
42
55
  emit("CANNED_RESPONSE_SELECT", activeCannedIndex);
@@ -1 +1 @@
1
- {"version":3,"file":"ToolbarPlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ToolbarPlugin.tsx"],"names":[],"mappings":"AA4DA,eAAO,MAAM,aAAa,GAAI,aAAqB;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,4CA2NvE,CAAC"}
1
+ {"version":3,"file":"ToolbarPlugin.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/ToolbarPlugin.tsx"],"names":[],"mappings":"AA6DA,eAAO,MAAM,aAAa,GAAI,aAAqB;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,4CAkOvE,CAAC"}
@@ -6,7 +6,7 @@ import { $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND
6
6
  import { $createQuoteNode, $isQuoteNode } from "@lexical/rich-text";
7
7
  import { TOGGLE_LINK_COMMAND } from "@lexical/link";
8
8
  import { useCallback, useEffect, useMemo, useState } from "react";
9
- import { BoldOutlined, ItalicOutlined, StrikethroughOutlined, OrderedListOutlined, UnorderedListOutlined, } from "@ant-design/icons";
9
+ import { BoldOutlined, ItalicOutlined, StrikethroughOutlined, UnderlineOutlined, OrderedListOutlined, UnorderedListOutlined, } from "@ant-design/icons";
10
10
  import { Button } from "antd";
11
11
  const quoteIcon = (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M5.24615 10.5714C4.5616 10.5714 3.99078 10.5715 3.53762 10.5105C3.0593 10.4462 2.62745 10.3047 2.28042 9.95769C1.9334 9.61072 1.79194 9.17887 1.72763 8.70055C1.6667 8.24737 1.66673 7.67654 1.66675 6.99199V6.91274C1.66673 6.22819 1.6667 5.65734 1.72763 5.20419C1.79194 4.72586 1.9334 4.29401 2.28043 3.94699C2.62746 3.59997 3.0593 3.4585 3.53762 3.3942C3.99078 3.33327 4.56161 3.33329 5.24615 3.33331H5.32541C6.00995 3.33329 6.5808 3.33327 7.03397 3.3942C7.51228 3.4585 7.94413 3.59997 8.29116 3.94699C8.63817 4.29401 8.77966 4.72586 8.84396 5.20419C8.90491 5.65734 8.90484 6.22817 8.90484 6.91272V6.99198C8.90484 7.67652 8.90491 8.24737 8.84396 8.70055C8.77966 9.17887 8.63817 9.61072 8.29116 9.95769C7.94413 10.3047 7.51228 10.4462 7.03397 10.5105C6.5808 10.5715 6.00998 10.5714 5.32544 10.5714H5.24615Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.84403 16.1313C2.71884 15.7295 2.94301 15.3023 3.34474 15.1772C5.71249 14.4393 7.38095 12.3426 7.38095 9.914V6.76172C7.38095 6.34094 7.72207 5.99982 8.14286 5.99982C8.56366 5.99982 8.90476 6.34094 8.90476 6.76172V9.914C8.90476 13.0731 6.73976 15.7152 3.79812 16.6319C3.39638 16.7572 2.96923 16.533 2.84403 16.1313Z", fill: "currentColor" }), _jsx("path", { d: "M14.389 10.5714C13.7044 10.5714 13.1336 10.5715 12.6804 10.5105C12.2021 10.4462 11.7703 10.3047 11.4233 9.95769C11.0762 9.61072 10.9348 9.17887 10.8704 8.70055C10.8095 8.24737 10.8096 7.67654 10.8096 6.99199V6.91274C10.8096 6.22819 10.8095 5.65734 10.8704 5.20419C10.9348 4.72586 11.0762 4.29401 11.4233 3.94699C11.7703 3.59997 12.2021 3.4585 12.6804 3.3942C13.1336 3.33327 13.7044 3.33329 14.389 3.33331H14.4682C15.1528 3.33329 15.7236 3.33327 16.1768 3.3942C16.6551 3.4585 17.087 3.59997 17.4339 3.94699C17.781 4.29401 17.9225 4.72586 17.9868 5.20419C18.0477 5.65734 18.0477 6.22817 18.0477 6.91272V6.99198C18.0477 7.67652 18.0477 8.24737 17.9868 8.70055C17.9225 9.17887 17.781 9.61072 17.4339 9.95769C17.087 10.3047 16.6551 10.4462 16.1768 10.5105C15.7236 10.5715 15.1528 10.5714 14.4682 10.5714H14.389Z", fill: "currentColor" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M11.9869 16.1313C11.8617 15.7295 12.0858 15.3023 12.4876 15.1772C14.8553 14.4393 16.5238 12.3426 16.5238 9.914V6.76172C16.5238 6.34094 16.8649 5.99982 17.2857 5.99982C17.7065 5.99982 18.0476 6.34094 18.0476 6.76172V9.914C18.0476 13.0731 15.8825 15.7152 12.9409 16.6319C12.5392 16.7572 12.112 16.533 11.9869 16.1313Z", fill: "currentColor" })] }));
12
12
  export const ToolbarPlugin = ({ compact = false }) => {
@@ -26,6 +26,12 @@ export const ToolbarPlugin = ({ compact = false }) => {
26
26
  onPress: () => formatText("italic"),
27
27
  isActive: activeMarks.includes("italic"),
28
28
  },
29
+ {
30
+ key: "underline",
31
+ icon: _jsx(UnderlineOutlined, { size: 22 }),
32
+ onPress: () => formatText("underline"),
33
+ isActive: activeMarks.includes("underline"),
34
+ },
29
35
  {
30
36
  key: "strikethrough",
31
37
  icon: _jsx(StrikethroughOutlined, { size: 22 }),
@@ -120,6 +126,8 @@ export const ToolbarPlugin = ({ compact = false }) => {
120
126
  marks.push("bold");
121
127
  if (selection.hasFormat("italic"))
122
128
  marks.push("italic");
129
+ if (selection.hasFormat("underline"))
130
+ marks.push("underline");
123
131
  if (selection.hasFormat("strikethrough"))
124
132
  marks.push("strikethrough");
125
133
  // block
@@ -1 +1 @@
1
- {"version":3,"file":"resetEditorAfterSend.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/resetEditorAfterSend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAoE,MAAM,SAAS,CAAC;AAW1G,eAAO,MAAM,sBAAsB,GACjC,QAAQ,aAAa,EACrB,MAAM;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,SAU9C,CAAC;AAIF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,aAAa,SAgDzD,CAAC"}
1
+ {"version":3,"file":"resetEditorAfterSend.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/resetEditorAfterSend.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAoE,MAAM,SAAS,CAAC;AAW1G,eAAO,MAAM,sBAAsB,GACjC,QAAQ,aAAa,EACrB,MAAM;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,SAU9C,CAAC;AAIF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,aAAa,SA4CzD,CAAC"}
@@ -16,8 +16,8 @@ export const resetEditorIfUnchanged = (editor, sent) => {
16
16
  return;
17
17
  resetEditorAfterSend(editor);
18
18
  };
19
- // Empties the composer after a send but keeps the typing format, list and quote
20
- // context, so the next message continues in the same block style.
19
+ // Empties the composer after a send. Inline marks are cleared; the list and
20
+ // quote context stays so the next message continues in the same block style.
21
21
  export const resetEditorAfterSend = (editor) => {
22
22
  clearComposerHistoryAfterUpdate(editor);
23
23
  editor.update(() => {
@@ -35,15 +35,11 @@ export const resetEditorAfterSend = (editor) => {
35
35
  const listType = isListNode
36
36
  ? topLevelNode.getListType()
37
37
  : undefined;
38
- if (selection.hasFormat("bold")) {
39
- selection.formatText("bold");
40
- }
41
- if (selection.hasFormat("italic")) {
42
- selection.formatText("italic");
43
- }
44
- if (selection.hasFormat("strikethrough")) {
45
- selection.formatText("strikethrough");
46
- }
38
+ // paragraph.select() keeps the previous selection's typing format, so a
39
+ // mark switched on before sending (bold, italic, underline...) would carry
40
+ // into the next message. Drop every mark instead of toggling known ones.
41
+ selection.format = 0;
42
+ selection.style = "";
47
43
  if (isQuoteNode) {
48
44
  // Nếu đang là QuoteNode → chuyển về Paragraph (bình thường)
49
45
  const paragraph = $createParagraphNode();
@@ -186,7 +186,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
186
186
  const isSystemLogMessage = isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType);
187
187
  const isGroupNotificationMessage = isGroupNotificationMessageType(message === null || message === void 0 ? void 0 : message.contentType);
188
188
  const isSessionEvent = isSessionEventMessage(message);
189
- const showsSessionEvents = isCrm || isSessionAgentView;
189
+ const showsSessionEvents = isCrm;
190
190
  const isStickerMessage = (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.StickerMessage;
191
191
  const hasContextMenu = contextMenuItems.length > 0;
192
192
  const isLatestMine = ((_a = allMessages.find((m) => m.sendID === userID)) === null || _a === void 0 ? void 0 : _a.clientMsgID) ===
@@ -265,7 +265,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
265
265
  memberRun ? (_jsx(MemberEventSummary, { messages: memberRun.messages })) : (_jsx(GroupNotificationMessageItem, { message: message }))));
266
266
  }
267
267
  if (isSessionEvent) {
268
- // Owners in consumer apps don't see session dividers; CRM and agents do.
268
+ // Only CRM shows session dividers; consumer apps and PDP agents do not.
269
269
  if (!showsSessionEvents)
270
270
  return hiddenMessageNode;
271
271
  return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Empty, Spin } from "antd";
4
4
  import { useTranslation } from "react-i18next";
5
- import InfiniteScroll from "react-infinite-scroll-component";
5
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
6
6
  import SearchItemAsGroup from "./item/SearchItemAsGroup";
7
7
  import { useSearchGroups } from "../../hooks/search/useSearchGroups";
8
8
  import { useSearchScope } from "./SearchScopeContext";
@@ -3,7 +3,7 @@ import { Empty, Spin } from "antd";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
5
5
  import { MessageType } from "@droppii-org/wasm-client-sdk";
6
- import InfiniteScroll from "react-infinite-scroll-component";
6
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
7
7
  import SearchItemAsMessage from "./item/SearchItemAsMessage";
8
8
  import { useSearchScope } from "./SearchScopeContext";
9
9
  const SearchConversationAsMessages = (props) => {
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Empty, Spin } from "antd";
3
3
  import { useTranslation } from "react-i18next";
4
- import InfiniteScroll from "react-infinite-scroll-component";
4
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
5
5
  import SearchItemAsUser from "./item/SearchItemAsUser";
6
6
  import { useSearchUsers } from "../../hooks/search/useSearchUsers";
7
7
  import { useSearchScope } from "./SearchScopeContext";
@@ -3,7 +3,7 @@ import { Empty, Spin } from "antd";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { useSearchMessage } from "../../hooks/search/useSearchMessage";
5
5
  import { MessageType } from "@droppii-org/wasm-client-sdk";
6
- import InfiniteScroll from "react-infinite-scroll-component";
6
+ import InfiniteScroll from "../common/ShadowSafeInfiniteScroll";
7
7
  import SearchItemAsMessage from "./item/SearchItemAsMessage";
8
8
  import useConversationStore from "../../store/conversation";
9
9
  const SearchMessageOnCurrentConversation = (props) => {
@@ -4,7 +4,7 @@ export declare const isMessageHiddenForViewer: (message: MessageItem, isCrm: boo
4
4
  peerType?: string;
5
5
  conversationType?: number;
6
6
  ex?: string;
7
- }, viewerUserID?: string) => boolean;
7
+ }, _viewerUserID?: string) => boolean;
8
8
  export declare const useMessage: (conversationId?: string, searchClientMsgID?: string, enabled?: boolean, useAppHistory?: boolean, onSearchMessageRevoked?: () => void) => {
9
9
  loadState: {
10
10
  initLoading: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EAEX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAkBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA4BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;uCA6BlB,MAAM;CAscvB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAkCF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
1
+ {"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EAEX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAiBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,gBAAgB,MAAM,KACrB,OA2BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;uCA6BlB,MAAM;CAscvB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAkCF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
@@ -12,7 +12,6 @@ import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
12
12
  import { GROUP_NOTIFICATION_MESSAGE_TYPES, isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType } from "../../utils/messageLog";
13
13
  import { isEventCustomMessage, isRenderableCustomMessage, } from "../../utils/customMessage";
14
14
  import { isGroupConversation } from "../../utils/imCommon";
15
- import { isSupportSessionAgentView } from "../../utils/conversation";
16
15
  export const visibleTypeMessage = [
17
16
  MessageType.TextMessage,
18
17
  MessageType.PictureMessage,
@@ -32,7 +31,7 @@ export const visibleTypeMessage = [
32
31
  MessageType.LogTextMessage,
33
32
  ...GROUP_NOTIFICATION_MESSAGE_TYPES,
34
33
  ];
35
- export const isMessageHiddenForViewer = (message, isCrm, conversation, viewerUserID) => {
34
+ export const isMessageHiddenForViewer = (message, isCrm, conversation, _viewerUserID) => {
36
35
  if (!visibleTypeMessage.includes(message === null || message === void 0 ? void 0 : message.contentType))
37
36
  return true;
38
37
  // Group notification messages stay hidden in a CX support session even
@@ -45,14 +44,13 @@ export const isMessageHiddenForViewer = (message, isCrm, conversation, viewerUse
45
44
  // never sees them.
46
45
  if (isCrm)
47
46
  return isEventCustomMessage(message);
48
- // An agent handling someone else's session (e.g. PDP CS) needs the
49
- // assignment log and session dividers; the owner never sees them.
50
- const isAgentView = isSupportSessionAgentView(conversation, viewerUserID);
51
- if (isAgentView && isSessionEventMessage(message))
52
- return false;
47
+ // Outside CRM the viewer is either the session owner or an agent handling the
48
+ // owner's session (PDP Portal). Neither sees session management: the
49
+ // assignment log and the session start/end dividers stay hidden.
53
50
  const isCustomMessage = MessageType.CustomMessage === (message === null || message === void 0 ? void 0 : message.contentType);
54
51
  return (Boolean(message === null || message === void 0 ? void 0 : message.isInternal) ||
55
- (!isAgentView && isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType)) ||
52
+ isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType) ||
53
+ isSessionEventMessage(message) ||
56
54
  (isCustomMessage && !isRenderableCustomMessage(message)));
57
55
  };
58
56
  export const useMessage = (conversationId, searchClientMsgID, enabled = true, useAppHistory = false,
@@ -1 +1 @@
1
- {"version":3,"file":"useMessageListScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessageListScroll.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,WAAW,EAAe,MAAM,8BAA8B,CAAC;AAYxE,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,UAAU,0BAA0B;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC,eAAe,EAAE,SAAS,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,oBAAoB,GAAI,sLAYlC,0BAA0B;;;;8BAsErB,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;;;mCA0Ce,MAAM;CAyEzD,CAAC"}
1
+ {"version":3,"file":"useMessageListScroll.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessageListScroll.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAClE,OAAO,EAAE,WAAW,EAAe,MAAM,8BAA8B,CAAC;AAYxE,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED,UAAU,0BAA0B;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,oBAAoB,CAAC;IAChC,eAAe,EAAE,SAAS,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,kBAAkB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,uFAAuF;IACvF,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrD;AAED,eAAO,MAAM,oBAAoB,GAAI,sLAYlC,0BAA0B;;;;8BAsErB,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC;;;mCA0Ce,MAAM;CAkFzD,CAAC"}
@@ -101,6 +101,13 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
101
101
  });
102
102
  }, [handleMarkConversationMessageAsRead]);
103
103
  const scrollToMessage = useCallback((clientMsgID) => {
104
+ // Resolve in the tree the list lives in: inside a shadow root (Mall),
105
+ // document.getElementById cannot see the message elements.
106
+ const getMessageElementById = (id) => {
107
+ var _a, _b, _c;
108
+ const root = (_a = scrollRef.current) === null || _a === void 0 ? void 0 : _a.getRootNode();
109
+ return ((_c = (_b = root === null || root === void 0 ? void 0 : root.getElementById) === null || _b === void 0 ? void 0 : _b.call(root, id)) !== null && _c !== void 0 ? _c : document.getElementById(id));
110
+ };
104
111
  // A search hit recalled since the results came back: say so instead of landing on its placeholder.
105
112
  // Checked before the scroll and again before the highlight, since the list reloads in between.
106
113
  const dropRevokedSearchTarget = () => {
@@ -117,7 +124,7 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
117
124
  setTimeout(() => {
118
125
  if (dropRevokedSearchTarget())
119
126
  return;
120
- const targetElement = document.getElementById(`${MSG_ITEM_PREFIX}${clientMsgID}`);
127
+ const targetElement = getMessageElementById(`${MSG_ITEM_PREFIX}${clientMsgID}`);
121
128
  targetElement === null || targetElement === void 0 ? void 0 : targetElement.scrollIntoView({
122
129
  behavior: "auto",
123
130
  block: "center",
@@ -126,10 +133,10 @@ export const useMessageListScroll = ({ conversationId, userId, isGroupChat, load
126
133
  var _a;
127
134
  if (dropRevokedSearchTarget())
128
135
  return;
129
- const targetContentElement = document.getElementById(`${MSG_ITEM_CONTENT_PREFIX}${clientMsgID}`);
136
+ const targetContentElement = getMessageElementById(`${MSG_ITEM_CONTENT_PREFIX}${clientMsgID}`);
130
137
  // The border hugs the bubble; the pulse runs on the wrapper that also
131
138
  // holds the pin badge, or the badge stays put while the bubble scales.
132
- const pulseElement = (_a = document.getElementById(`${MSG_ITEM_BUBBLE_GROUP_PREFIX}${clientMsgID}`)) !== null && _a !== void 0 ? _a : targetContentElement;
139
+ const pulseElement = (_a = getMessageElementById(`${MSG_ITEM_BUBBLE_GROUP_PREFIX}${clientMsgID}`)) !== null && _a !== void 0 ? _a : targetContentElement;
133
140
  const isHeld = useConversationStore.getState().heldHighlightClientMsgID ===
134
141
  clientMsgID;
135
142
  if (targetContentElement && isHeld) {
@@ -1 +1 @@
1
- {"version":3,"file":"useSyncGroupStateAfterJoin.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useSyncGroupStateAfterJoin.ts"],"names":[],"mappings":"AAaA,UAAU,gCAAgC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,0BAA0B,GAAI,cAExC,gCAAgC;;;CAmBlC,CAAC"}
1
+ {"version":3,"file":"useSyncGroupStateAfterJoin.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useSyncGroupStateAfterJoin.ts"],"names":[],"mappings":"AAeA,UAAU,gCAAgC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,0BAA0B,GAAI,cAExC,gCAAgC;;;CA+BlC,CAAC"}
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
  import { useCallback } from "react";
3
+ import { useQueryClient } from "@tanstack/react-query";
4
+ import { QUERY_KEYS } from "../../services/query";
3
5
  import useConversationStore from "../../store/conversation";
4
6
  import { useIsJoinedGroup } from "./useIsJoinedGroup";
5
7
  const JOIN_GROUP_SYNC_DELAY = 500;
@@ -8,14 +10,25 @@ const waitForJoinGroupSync = () => new Promise((resolve) => {
8
10
  });
9
11
  export const useSyncGroupStateAfterJoin = ({ groupId, }) => {
10
12
  const getCurrentGroupInfoByReq = useConversationStore((state) => state.getCurrentGroupInfoByReq);
13
+ const queryClient = useQueryClient();
11
14
  const { isJoined, refetch: refetchIsJoined } = useIsJoinedGroup(groupId);
12
15
  const syncGroupStateAfterJoin = useCallback(async () => {
13
16
  await waitForJoinGroupSync();
14
- await refetchIsJoined();
17
+ const { data: joinedLocally } = await refetchIsJoined();
15
18
  if (!groupId)
16
19
  return;
20
+ // Only called once the join REST call succeeded. Re-joining a group that
21
+ // is still cached locally (the agent left earlier) syncs as an update, so
22
+ // no OnJoinedGroupAdded fires and the local member lookup can stay empty
23
+ // for seconds — nothing would re-check, leaving the Join button up until
24
+ // F5. The server already confirmed membership, so trust it.
25
+ if (!joinedLocally) {
26
+ const queryKey = [QUERY_KEYS.IS_JOINED_GROUP, groupId];
27
+ await queryClient.cancelQueries({ queryKey });
28
+ queryClient.setQueryData(queryKey, true);
29
+ }
17
30
  await getCurrentGroupInfoByReq(groupId);
18
- }, [getCurrentGroupInfoByReq, groupId, refetchIsJoined]);
31
+ }, [getCurrentGroupInfoByReq, groupId, queryClient, refetchIsJoined]);
19
32
  return {
20
33
  isJoined,
21
34
  syncGroupStateAfterJoin,
@@ -9,6 +9,7 @@
9
9
  "media_resources_title": "Tập tin phương tiện",
10
10
  "no_resource_files": "Không tìm thấy tập tin nào",
11
11
  "no_resource_links": "Không tìm thấy liên kết nào",
12
+ "no_thread_links": "Không có liên kết nào",
12
13
  "previous_video": "Video trước",
13
14
  "next_video": "Video tiếp theo",
14
15
  "resource_load_error": "Không thể tải dữ liệu. Vui lòng thử lại.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droppii-org/chat-sdk",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
4
4
  "description": "Droppii React Chat SDK",
5
5
  "repository": {
6
6
  "type": "git",