@droppii-org/chat-sdk 0.4.7 → 0.4.9
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/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +8 -2
- package/dist/components/conversation-detail/ConversationDetailInput.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationDetailInput.js +21 -2
- package/dist/components/conversation-detail/mention/BizMentionMenu.d.ts +3 -1
- package/dist/components/conversation-detail/mention/BizMentionMenu.d.ts.map +1 -1
- package/dist/components/conversation-detail/mention/BizMentionMenu.js +4 -2
- package/dist/components/conversation-detail/mention/useMentionTokens.d.ts +1 -0
- package/dist/components/conversation-detail/mention/useMentionTokens.d.ts.map +1 -1
- package/dist/components/conversation-detail/mention/useMentionTokens.js +4 -0
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +3 -1
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +2 -2
- package/dist/components/message/footer/mention/MentionMenu.d.ts +1 -0
- package/dist/components/message/footer/mention/MentionMenu.d.ts.map +1 -1
- package/dist/components/message/footer/mention/MentionMenu.js +76 -6
- package/dist/components/message/footer/mention/MentionNode.d.ts +1 -0
- package/dist/components/message/footer/mention/MentionNode.d.ts.map +1 -1
- package/dist/components/message/footer/mention/MentionNode.js +15 -0
- package/dist/components/message/footer/mention/useMentionOptions.d.ts.map +1 -1
- package/dist/components/message/footer/mention/useMentionOptions.js +6 -3
- package/dist/components/message/item/SessionEventMessage.d.ts +7 -0
- package/dist/components/message/item/SessionEventMessage.d.ts.map +1 -0
- package/dist/components/message/item/SessionEventMessage.js +12 -0
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +10 -2
- package/dist/components/search-conversation/index.d.ts.map +1 -1
- package/dist/components/search-conversation/index.js +20 -2
- package/dist/components/search-conversation/item/SearchItemAsGroup.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsGroup.js +2 -1
- package/dist/components/search-conversation/item/SearchItemAsMessage.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsMessage.js +4 -1
- package/dist/hooks/conversation/useResolveConversation.d.ts.map +1 -1
- package/dist/hooks/conversation/useResolveConversation.js +2 -1
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +10 -1
- package/dist/hooks/group/useLeaveGroupAction.d.ts.map +1 -1
- package/dist/hooks/group/useLeaveGroupAction.js +47 -3
- package/dist/hooks/search/useSearchMessage.d.ts.map +1 -1
- package/dist/hooks/search/useSearchMessage.js +11 -1
- package/dist/locales/vi/common.json +5 -4
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +22 -9
- package/dist/store/type.d.ts +6 -2
- package/dist/store/type.d.ts.map +1 -1
- package/dist/styles/global.css +1 -1
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +7 -5
- package/dist/utils/messageLog.d.ts +4 -0
- package/dist/utils/messageLog.d.ts.map +1 -1
- package/dist/utils/messageLog.js +31 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+
|
|
1
|
+
{"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+CA2UzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -90,8 +90,12 @@ const DeskConversationList = () => {
|
|
|
90
90
|
}
|
|
91
91
|
setSelectedConversationId(threadId);
|
|
92
92
|
const selectedConversation = conversationList.find((conv) => conv.conversationID === threadId);
|
|
93
|
+
// threadId can come from a notification/stale search click — force a
|
|
94
|
+
// fresh membership check instead of trusting the groupID cache.
|
|
93
95
|
if (selectedConversation) {
|
|
94
|
-
setConversationData(selectedConversation
|
|
96
|
+
setConversationData(selectedConversation, undefined, {
|
|
97
|
+
forceRevalidate: true,
|
|
98
|
+
});
|
|
95
99
|
}
|
|
96
100
|
else {
|
|
97
101
|
DChatSDK.getMultipleConversation([threadId]).then((res) => {
|
|
@@ -99,7 +103,9 @@ const DeskConversationList = () => {
|
|
|
99
103
|
// (e.g. the user was removed from that group mid-flight) — only
|
|
100
104
|
// apply the result if the URL still points at this conversation.
|
|
101
105
|
if (res.data.length > 0 && searchParams.get("threadId") === threadId) {
|
|
102
|
-
setConversationData(res.data[0]
|
|
106
|
+
setConversationData(res.data[0], undefined, {
|
|
107
|
+
forceRevalidate: true,
|
|
108
|
+
});
|
|
103
109
|
updateConversationList(res.data, "filter");
|
|
104
110
|
}
|
|
105
111
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAkB/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,4CAynB9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -26,6 +26,7 @@ import { buildMentionPayload } from "./mention/buildMentionPayload";
|
|
|
26
26
|
const MAX_LINES = 5;
|
|
27
27
|
const CONTAINER_MIN_HEIGHT = 48;
|
|
28
28
|
const FALLBACK_LINE_HEIGHT = 25.6;
|
|
29
|
+
const MAX_MENTIONED_USERS = 10;
|
|
29
30
|
const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, moreOptions, onOpenBotMenu, }) => {
|
|
30
31
|
var _a;
|
|
31
32
|
const { t } = useTranslation("conversation-inbox");
|
|
@@ -62,10 +63,22 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
|
|
|
62
63
|
const mentionTrigger = (rawMentionTrigger === null || rawMentionTrigger === void 0 ? void 0 : rawMentionTrigger.triggerStart) === dismissedTriggerStart
|
|
63
64
|
? null
|
|
64
65
|
: rawMentionTrigger;
|
|
65
|
-
const
|
|
66
|
+
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 : "");
|
|
66
67
|
const isMentionMenuOpen = mentionTrigger !== null;
|
|
67
68
|
const [selectedMentionIndex, setSelectedMentionIndex] = useState(0);
|
|
68
69
|
const { tokens: mentionTokens, addToken, onValueChange, resetTokens } = useMentionTokens();
|
|
70
|
+
// "Tất cả" doesn't count against the per-user cap and stays selectable;
|
|
71
|
+
// only specific-member options are hidden once the limit is hit.
|
|
72
|
+
const mentionedUserIDs = useMemo(() => new Set(mentionTokens.filter((token) => !token.isAtAll).map((token) => token.userID)), [mentionTokens]);
|
|
73
|
+
const mentionedUsersCount = mentionedUserIDs.size;
|
|
74
|
+
const mentionLimitReached = mentionedUsersCount >= MAX_MENTIONED_USERS;
|
|
75
|
+
// Already-tagged users don't need to appear again in the same draft.
|
|
76
|
+
const mentionOptions = useMemo(() => {
|
|
77
|
+
const notYetMentioned = rawMentionOptions.filter((option) => option.isAtAll || !mentionedUserIDs.has(option.userID));
|
|
78
|
+
return mentionLimitReached
|
|
79
|
+
? notYetMentioned.filter((option) => option.isAtAll)
|
|
80
|
+
: notYetMentioned;
|
|
81
|
+
}, [rawMentionOptions, mentionedUserIDs, mentionLimitReached]);
|
|
69
82
|
const [mentionMenuPosition, setMentionMenuPosition] = useState(null);
|
|
70
83
|
const previewUrl = useMemo(() => {
|
|
71
84
|
const urls = extractLinkUrls(value);
|
|
@@ -115,6 +128,12 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
|
|
|
115
128
|
useEffect(() => {
|
|
116
129
|
setSelectedMentionIndex(0);
|
|
117
130
|
}, [mentionTrigger === null || mentionTrigger === void 0 ? void 0 : mentionTrigger.triggerStart, mentionTrigger === null || mentionTrigger === void 0 ? void 0 : mentionTrigger.query]);
|
|
131
|
+
// The cap can shrink mentionOptions independent of the trigger identity
|
|
132
|
+
// (reaching the limit filters out non-"Tất cả" entries) — clamp so a
|
|
133
|
+
// stale index left over from a longer list doesn't point out of range.
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
setSelectedMentionIndex((prev) => prev >= mentionOptions.length ? Math.max(0, mentionOptions.length - 1) : prev);
|
|
136
|
+
}, [mentionOptions.length]);
|
|
118
137
|
useEffect(() => {
|
|
119
138
|
const textarea = textareaRef.current;
|
|
120
139
|
if (!isMentionMenuOpen || !textarea) {
|
|
@@ -396,6 +415,6 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
|
|
|
396
415
|
: "border-gray-50"), style: { minHeight: CONTAINER_MIN_HEIGHT }, children: _jsxs("div", { className: "flex min-w-0 flex-[1_0_0] items-center gap-2 self-stretch px-3 py-1", children: [onOpenBotMenu && (_jsx("button", { type: "button", onClick: onOpenBotMenu, "aria-label": t("thread_detail.bot.menu.open_menu"), "data-testid": "conversation-detail-open-bot-menu", className: "flex shrink-0 items-center justify-center", children: _jsx(Icon, { icon: "chat-suggest-o", size: 24, className: "text-gray-600" }) })), _jsx("textarea", { ref: textareaRef, value: value, rows: 1, onChange: (event) => {
|
|
397
416
|
var _a;
|
|
398
417
|
return handleChange(event.target.value, (_a = event.target.selectionStart) !== null && _a !== void 0 ? _a : event.target.value.length);
|
|
399
|
-
}, onSelect: handleSelectionChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "conversation-detail-message-input", className: "conversation-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-gray-400 focus:border-0 focus:outline-none focus:ring-0" }), isMentionMenuOpen && mentionMenuPosition && (_jsx(BizMentionMenu, { options: mentionOptions, selectedIndex: selectedMentionIndex, onSelect: selectMentionOption, onHoverIndex: setSelectedMentionIndex, position: mentionMenuPosition, visible: true }))] }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "conversation-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-chat-send" : "bg-chat-send opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(ConversationMoreOptionPanel, { disabled: !isConversationReady, options: moreOptions, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, onTriggerDocumentInput: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
|
|
418
|
+
}, onSelect: handleSelectionChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t("thread_detail.input_placeholder"), "data-testid": "conversation-detail-message-input", className: "conversation-message-input-scrollbar min-h-0 min-w-0 flex-1 resize-none appearance-none border-0 bg-transparent p-0 text-[16px] leading-[1.6] tracking-[0.16px] text-black outline-none ring-0 placeholder:text-gray-400 focus:border-0 focus:outline-none focus:ring-0" }), isMentionMenuOpen && mentionMenuPosition && (_jsx(BizMentionMenu, { options: mentionOptions, selectedIndex: selectedMentionIndex, onSelect: selectMentionOption, onHoverIndex: setSelectedMentionIndex, position: mentionMenuPosition, limitReached: mentionLimitReached, limitMax: MAX_MENTIONED_USERS, visible: true }))] }) }), _jsx("div", { className: "flex shrink-0 items-center pb-1", children: _jsx("button", { type: "button", onClick: () => void handleSend(), disabled: !canSend, "data-testid": "conversation-detail-send", className: clsx("flex h-10 w-10 items-center justify-center rounded-full", canSend ? "bg-chat-send" : "bg-chat-send opacity-25"), children: _jsx(Icon, { icon: "send-b", size: 20, className: "text-white" }) }) })] }), isMoreOptionOpen && (_jsx(ConversationMoreOptionPanel, { disabled: !isConversationReady, options: moreOptions, onTriggerMediaInput: () => { var _a; return (_a = mediaInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, onTriggerDocumentInput: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); } }))] }));
|
|
400
419
|
};
|
|
401
420
|
export default ConversationDetailInput;
|
|
@@ -11,7 +11,9 @@ export interface BizMentionMenuProps {
|
|
|
11
11
|
direction: "up" | "down";
|
|
12
12
|
};
|
|
13
13
|
visible: boolean;
|
|
14
|
+
limitReached?: boolean;
|
|
15
|
+
limitMax?: number;
|
|
14
16
|
}
|
|
15
|
-
declare const BizMentionMenu: ({ options, selectedIndex, onSelect, onHoverIndex, position, visible, }: BizMentionMenuProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
17
|
+
declare const BizMentionMenu: ({ options, selectedIndex, onSelect, onHoverIndex, position, visible, limitReached, limitMax, }: BizMentionMenuProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
16
18
|
export default BizMentionMenu;
|
|
17
19
|
//# sourceMappingURL=BizMentionMenu.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BizMentionMenu.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/mention/BizMentionMenu.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAE3F,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;KAC1B,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"BizMentionMenu.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/mention/BizMentionMenu.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAE3F,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE;QACR,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;KAC1B,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,QAAA,MAAM,cAAc,GAAI,gGASrB,mBAAmB,mDA6DrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -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 { Avatar } from "antd";
|
|
5
|
-
const BizMentionMenu = ({ options, selectedIndex, onSelect, onHoverIndex, position, visible, }) => {
|
|
5
|
+
const BizMentionMenu = ({ options, selectedIndex, onSelect, onHoverIndex, position, visible, limitReached, limitMax = 10, }) => {
|
|
6
6
|
const { t } = useTranslation();
|
|
7
7
|
if (!visible)
|
|
8
8
|
return null;
|
|
@@ -15,6 +15,8 @@ const BizMentionMenu = ({ options, selectedIndex, onSelect, onHoverIndex, positi
|
|
|
15
15
|
const anchorStyle = position.direction === "up"
|
|
16
16
|
? { bottom: position.bottom, left: position.left }
|
|
17
17
|
: { top: position.top, left: position.left };
|
|
18
|
-
return (_jsxs("div", { "data-cp-name": "BizMentionMenu", "data-testid": "biz-mention-menu", className: "fixed z-50 max-h-[220px] w-[260px] overflow-y-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", style: anchorStyle, children: [_jsx("div", { className: "px-3 py-1 text-xs font-medium text-gray-400", children: t("mention.menu_title") }), options.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children:
|
|
18
|
+
return (_jsxs("div", { "data-cp-name": "BizMentionMenu", "data-testid": "biz-mention-menu", className: "fixed z-50 max-h-[220px] w-[260px] overflow-y-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", style: anchorStyle, children: [_jsx("div", { className: "px-3 py-1 text-xs font-medium text-gray-400", children: t("mention.menu_title") }), options.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children: limitReached
|
|
19
|
+
? t("mention.limit_reached", { max: limitMax })
|
|
20
|
+
: t("mention.no_result") })) : (options.map((option, index) => (_jsxs("button", { type: "button", "data-selected": index === selectedIndex, className: `flex w-full items-center gap-2 px-3 py-2 text-left text-sm ${index === selectedIndex ? "bg-blue-50" : "hover:bg-gray-50"}`, onMouseEnter: () => onHoverIndex(index), onClick: () => onSelect(option), children: [option.isAtAll ? (_jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gray-100 text-gray-500", children: "@" })) : (_jsx(Avatar, { size: 24, src: option.faceURL, children: option.displayName.charAt(0) })), _jsx("span", { className: "min-w-0 flex-1 truncate", children: option.displayName })] }, option.userID))))] }));
|
|
19
21
|
};
|
|
20
22
|
export default BizMentionMenu;
|
|
@@ -10,6 +10,7 @@ export interface EditRange {
|
|
|
10
10
|
deletedLength: number;
|
|
11
11
|
insertedLength: number;
|
|
12
12
|
}
|
|
13
|
+
export declare const countMentionedUsers: (tokens: MentionToken[]) => number;
|
|
13
14
|
export declare const computeEditRange: (oldValue: string, newValue: string) => EditRange;
|
|
14
15
|
export interface UseMentionTokensResult {
|
|
15
16
|
tokens: MentionToken[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMentionTokens.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/mention/useMentionTokens.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"useMentionTokens.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/mention/useMentionTokens.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAID,eAAO,MAAM,mBAAmB,GAAI,QAAQ,YAAY,EAAE,KAAG,MAEpD,CAAC;AAMV,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,SA4BF,CAAC;AA6BF,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,eAAO,MAAM,gBAAgB,QAAO,sBAqBnC,CAAC"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { useCallback, useState } from "react";
|
|
2
|
+
// Distinct specific users tagged so far — "Tất cả" (isAtAll) mentions aren't
|
|
3
|
+
// individual users and don't count toward the per-user cap.
|
|
4
|
+
export const countMentionedUsers = (tokens) => new Set(tokens.filter((token) => !token.isAtAll).map((token) => token.userID))
|
|
5
|
+
.size;
|
|
2
6
|
// Textarea edits are always a single contiguous range replacement (one
|
|
3
7
|
// keystroke, one backspace, or one paste/selection-replace) — so the exact
|
|
4
8
|
// edit range is derivable from the common prefix/suffix between old and new
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAgCA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAgCA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAwT3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -160,6 +160,8 @@ const MessageList = (props) => {
|
|
|
160
160
|
if (!conversationData) {
|
|
161
161
|
return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
|
|
162
162
|
}
|
|
163
|
-
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), (isGroupChat && isCheckingMembership) || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) :
|
|
163
|
+
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, "data-cp-name": "MessageList", children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), (isGroupChat && isCheckingMembership) || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : isViewOnlyMember ? (
|
|
164
|
+
// Brief fallback while isJoined catches up after the kick event.
|
|
165
|
+
_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t(isRestrictedByGroupMute ? "send_message_disabled_group_mute" : "send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(ConfirmModal, { open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, children: _jsx("p", { children: t("revoke_message_confirm_message") }) }), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
|
|
164
166
|
};
|
|
165
167
|
export default MessageList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAGL,wBAAwB,EAEzB,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAoB/C,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAc/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/footer/index.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAGL,wBAAwB,EAEzB,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAoB/C,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AASD,eAAO,MAAM,oBAAoB,mDAc/B,CAAC;AAEH,eAAO,MAAM,uBAAuB,gCAAyC,CAAC;AAE9E,QAAA,MAAM,qBAAqB,GAAI,4CAG5B,kBAAkB,4CA0UpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -27,7 +27,7 @@ import useConversationStore from "../../../store/conversation";
|
|
|
27
27
|
import { useCurrentUserAccountType } from "../../../hooks/user/useCurrentUserAccountType";
|
|
28
28
|
import ComposerTabs from "./ComposerTabs";
|
|
29
29
|
import ComposerEditor from "./ComposerEditor";
|
|
30
|
-
import MentionMenu from "./mention/MentionMenu";
|
|
30
|
+
import MentionMenu, { COMPOSER_AREA_CP_NAME } from "./mention/MentionMenu";
|
|
31
31
|
import SessionClosedModal from "./SessionClosedModal";
|
|
32
32
|
import { useCreateSession, useReopenSession, } from "../../../hooks/session/useReopenOrCreateSession";
|
|
33
33
|
import { emit } from "../../../utils/events";
|
|
@@ -234,6 +234,6 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
|
|
|
234
234
|
setIsMentionMenuOpen,
|
|
235
235
|
}, children: [_jsxs(LexicalComposer, { initialConfig: initialConfig, children: [_jsx(Popover, { open: isOpenCanned, content: _jsx(CannedResponse, { onClose: closeCanned, openCreateCannedModal: onOpenCreateCannedModal, cannedQuery: cannedQuery }), placement: "topLeft", trigger: "click", arrow: false, classNames: {
|
|
236
236
|
body: "mx-1 !p-0",
|
|
237
|
-
}, destroyOnHidden: true, children: _jsxs("div", { className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(ReplyPreviewBar, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setIsLinkPreviewDismissed(true) })), _jsx(ToolbarPlugin, {}), _jsxs("div", { children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, hintText: t("canned_response_hint"), isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), _jsx(ComposerEditor, { isInternal: isInternal, placeholderText: placeholderText })] }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(ComposerTextSyncPlugin, { onChange: setComposerText }), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isGroupConversation(conversationData !== null && conversationData !== void 0 ? conversationData : undefined) && (_jsx(MentionMenu, { groupID: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID })), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }), _jsx(SessionClosedModal, { open: isSessionClosedModalOpen, loading: isSessionActionLoading, onReopen: handleReopenSession, onCreateNew: handleCreateNewSession, onClose: handleCloseSessionModal })] }));
|
|
237
|
+
}, destroyOnHidden: true, children: _jsxs("div", { "data-cp-name": COMPOSER_AREA_CP_NAME, className: "border-t pb-2 flex flex-col gap-1 bg-white", children: [_jsx(ReplyPreviewBar, {}), listUploadFiles.length > 0 && _jsx(FilePreview, {}), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setIsLinkPreviewDismissed(true) })), _jsx(ToolbarPlugin, {}), _jsxs("div", { children: [_jsx(ComposerTabs, { canUseInternal: canUseInternal, hintText: t("canned_response_hint"), isInternal: isInternal, lockToInternal: lockToInternal, setIsInternal: setIsInternal }), _jsx(ComposerEditor, { isInternal: isInternal, placeholderText: placeholderText })] }), _jsx(ActionBar, {})] }) }), _jsx(LinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(ComposerTextSyncPlugin, { onChange: setComposerText }), _jsx(EnterHandler, {}), _jsx(PasteAndDropPlugin, {}), isGroupConversation(conversationData !== null && conversationData !== void 0 ? conversationData : undefined) && (_jsx(MentionMenu, { groupID: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID })), isCrm && (_jsx(CannedResponseTriggerPlugin, { onClose: closeCanned, onOpen: onOpenCanned }))] }), _jsx(SessionClosedModal, { open: isSessionClosedModalOpen, loading: isSessionActionLoading, onReopen: handleReopenSession, onCreateNew: handleCreateNewSession, onClose: handleCloseSessionModal })] }));
|
|
238
238
|
};
|
|
239
239
|
export default MessageFooterProvider;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentionMenu.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/MentionMenu.tsx"],"names":[],"mappings":"
|
|
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;CAC7B;AAED,QAAA,MAAM,WAAW,GAAI,aAAa,gBAAgB,mDAyIjD,CAAC;AA+CF,eAAe,WAAW,CAAC"}
|
|
@@ -1,15 +1,32 @@
|
|
|
1
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
|
+
};
|
|
2
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useMemo, useState } from "react";
|
|
14
|
+
import { useCallback, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
4
15
|
import { createPortal } from "react-dom";
|
|
5
16
|
import { useTranslation } from "react-i18next";
|
|
6
17
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
7
18
|
import { LexicalTypeaheadMenuPlugin, MenuOption, useBasicTypeaheadTriggerMatch, } from "@lexical/react/LexicalTypeaheadMenuPlugin";
|
|
8
|
-
import { $createTextNode } from "lexical";
|
|
9
|
-
import { $createMentionNode } from "./MentionNode";
|
|
19
|
+
import { $createTextNode, $getRoot } from "lexical";
|
|
20
|
+
import { $createMentionNode, $getMentionedUserIDs } from "./MentionNode";
|
|
10
21
|
import { useMentionOptions } from "./useMentionOptions";
|
|
11
22
|
import { Avatar } from "antd";
|
|
12
23
|
import { useMessageFooterContext } from "..";
|
|
24
|
+
const MAX_MENTIONED_USERS = 10;
|
|
25
|
+
// Marks the composer's outer boundary (footer/index.tsx) so
|
|
26
|
+
// MentionMenuOffset below can measure against it despite Lexical's
|
|
27
|
+
// typeahead container being portaled outside the composer's own DOM
|
|
28
|
+
// subtree. Exported so both sides of this coupling share one literal.
|
|
29
|
+
export const COMPOSER_AREA_CP_NAME = "MessageFooterComposerArea";
|
|
13
30
|
class MentionTypeaheadOption extends MenuOption {
|
|
14
31
|
constructor(option) {
|
|
15
32
|
super(option.userID);
|
|
@@ -25,7 +42,21 @@ const MentionMenu = ({ groupID }) => {
|
|
|
25
42
|
minLength: 0,
|
|
26
43
|
});
|
|
27
44
|
const options = useMentionOptions(groupID, query !== null && query !== void 0 ? query : "");
|
|
28
|
-
|
|
45
|
+
// Re-evaluated per query change (menu re-opens/re-queries on every "@"),
|
|
46
|
+
// so this stays in sync with mentions added/removed since the last count.
|
|
47
|
+
const mentionedUserIDs = useMemo(() => editor.getEditorState().read(() => $getMentionedUserIDs($getRoot())), [editor, query]);
|
|
48
|
+
const mentionedUsersCount = mentionedUserIDs.size;
|
|
49
|
+
const limitReached = mentionedUsersCount >= MAX_MENTIONED_USERS;
|
|
50
|
+
// Already-tagged users don't need to appear again in the same draft.
|
|
51
|
+
// "Tất cả" doesn't count against the per-user cap and stays selectable;
|
|
52
|
+
// only specific-member options are hidden once the limit is hit.
|
|
53
|
+
const visibleOptions = useMemo(() => {
|
|
54
|
+
const notYetMentioned = options.filter((option) => option.isAtAll || !mentionedUserIDs.has(option.userID));
|
|
55
|
+
return limitReached
|
|
56
|
+
? notYetMentioned.filter((option) => option.isAtAll)
|
|
57
|
+
: notYetMentioned;
|
|
58
|
+
}, [options, mentionedUserIDs, limitReached]);
|
|
59
|
+
const typeaheadOptions = useMemo(() => visibleOptions.map((option) => new MentionTypeaheadOption(option)), [visibleOptions]);
|
|
29
60
|
const onSelectOption = useCallback((selected, textNodeContainingQuery, closeMenu) => {
|
|
30
61
|
editor.update(() => {
|
|
31
62
|
const mentionNode = $createMentionNode(selected.option.userID, selected.option.isAtAll, `@${selected.option.displayName}`);
|
|
@@ -52,8 +83,47 @@ const MentionMenu = ({ groupID }) => {
|
|
|
52
83
|
// that much room, so it always renders downward and gets clipped
|
|
53
84
|
// by the page. Anchoring this menu to the container's bottom
|
|
54
85
|
// edge instead makes it grow upward from the caret line
|
|
55
|
-
// unconditionally, independent of that flip heuristic.
|
|
56
|
-
|
|
86
|
+
// unconditionally, independent of that flip heuristic. But the
|
|
87
|
+
// caret line itself sits inside the composer box (it's the text
|
|
88
|
+
// cursor position), not above it — so "grow up from here" still
|
|
89
|
+
// overlapped the composer's own top portion. MentionMenuOffset
|
|
90
|
+
// pushes the extra distance from the caret up to the composer's
|
|
91
|
+
// actual top edge, so the menu clears the input entirely.
|
|
92
|
+
_jsxs(MentionMenuOffset, { "data-cp-name": "MentionMenu", className: "absolute left-0 z-50 max-h-[220px] w-[260px] overflow-y-auto rounded-md border border-gray-200 bg-white py-1 shadow-lg", children: [_jsx("div", { className: "px-3 py-1 text-xs font-medium text-gray-400", children: t("mention.menu_title") }), typeaheadOptions.length === 0 ? (_jsx("div", { className: "px-3 py-2 text-sm text-gray-500", children: limitReached
|
|
93
|
+
? t("mention.limit_reached", { max: MAX_MENTIONED_USERS })
|
|
94
|
+
: t("mention.no_result") })) : (typeaheadOptions.map((option, index) => (_jsxs("button", { type: "button", className: `flex w-full items-center gap-2 px-3 py-2 text-left text-sm ${index === selectedIndex ? "bg-blue-50" : "hover:bg-gray-50"}`, onMouseEnter: () => setHighlightedIndex(index), onClick: () => selectOptionAndCleanUp(option), children: [option.option.isAtAll ? (_jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-gray-100 text-gray-500", children: "@" })) : (_jsx(Avatar, { size: 24, src: option.option.faceURL, children: option.option.displayName.charAt(0) })), _jsx("span", { className: "min-w-0 flex-1 truncate", children: option.option.displayName })] }, option.key))))] }), anchorElementRef.current);
|
|
57
95
|
} }));
|
|
58
96
|
};
|
|
97
|
+
// Renders inside Lexical's own typeahead container, which it portals
|
|
98
|
+
// straight to document.body — detached from the composer's own DOM
|
|
99
|
+
// subtree, so `closest()` can't find the composer from here. Look it up
|
|
100
|
+
// globally instead: only one composer is ever focused/active at a time.
|
|
101
|
+
// Measures the distance from the caret-position anchor up to the
|
|
102
|
+
// composer's top edge and applies it as `bottom`, so the menu's own bottom
|
|
103
|
+
// edge lands there instead of at the caret's — clearing the input instead
|
|
104
|
+
// of overlapping it.
|
|
105
|
+
//
|
|
106
|
+
// No dependency array: this component's identity survives across the
|
|
107
|
+
// re-renders LexicalTypeaheadMenuPlugin fires on every keystroke while the
|
|
108
|
+
// menu stays open (same tree position, not remounted), so a one-time
|
|
109
|
+
// measurement would go stale the moment the composer's own height changes
|
|
110
|
+
// mid-session (e.g. a reply preview or file attachment toggling on/off).
|
|
111
|
+
// Re-measuring every render keeps it correct at the cost of one cheap
|
|
112
|
+
// getBoundingClientRect() call per keystroke.
|
|
113
|
+
const MentionMenuOffset = (_a) => {
|
|
114
|
+
var { className, children } = _a, rest = __rest(_a, ["className", "children"]);
|
|
115
|
+
const ref = useRef(null);
|
|
116
|
+
const [bottomOffset, setBottomOffset] = useState(0);
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
var _a;
|
|
119
|
+
const anchorNode = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.parentElement;
|
|
120
|
+
const composer = document.querySelector(`[data-cp-name="${COMPOSER_AREA_CP_NAME}"]`);
|
|
121
|
+
if (!anchorNode || !composer)
|
|
122
|
+
return;
|
|
123
|
+
const anchorRect = anchorNode.getBoundingClientRect();
|
|
124
|
+
const composerRect = composer.getBoundingClientRect();
|
|
125
|
+
setBottomOffset(Math.max(0, anchorRect.top - composerRect.top));
|
|
126
|
+
});
|
|
127
|
+
return (_jsx("div", Object.assign({ ref: ref, className: className, style: { bottom: bottomOffset } }, rest, { children: children })));
|
|
128
|
+
};
|
|
59
129
|
export default MentionMenu;
|
|
@@ -17,4 +17,5 @@ export declare class MentionNode extends TextNode {
|
|
|
17
17
|
}
|
|
18
18
|
export declare function $createMentionNode(userID: string, isAtAll: boolean, displayText: string): MentionNode;
|
|
19
19
|
export declare function $isMentionNode(node: LexicalNode | null | undefined): node is MentionNode;
|
|
20
|
+
export declare function $getMentionedUserIDs(root: LexicalNode): Set<string>;
|
|
20
21
|
//# sourceMappingURL=MentionNode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MentionNode.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/MentionNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CACxC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,EACD,kBAAkB,CACnB,CAAC;AAEF,qBAAa,WAAY,SAAQ,QAAQ;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW;gBAU1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,OAAO;IAOf,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAM5C,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,qBAAqB,GAAG,WAAW;IAQrE,UAAU,IAAI,qBAAqB;IASnC,mBAAmB,IAAI,OAAO;IAI9B,YAAY,IAAI,IAAI;CAGrB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,WAAW,CAIb;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GACnC,IAAI,IAAI,WAAW,CAErB"}
|
|
1
|
+
{"version":3,"file":"MentionNode.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/MentionNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CACxC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB,EACD,kBAAkB,CACnB,CAAC;AAEF,qBAAa,WAAY,SAAQ,QAAQ;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IAEnB,MAAM,CAAC,OAAO,IAAI,MAAM;IAIxB,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW;gBAU1C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,OAAO;IAOf,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW;IAM5C,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,qBAAqB,GAAG,WAAW;IAQrE,UAAU,IAAI,qBAAqB;IASnC,mBAAmB,IAAI,OAAO;IAI9B,YAAY,IAAI,IAAI;CAGrB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,WAAW,CAIb;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GACnC,IAAI,IAAI,WAAW,CAErB;AAID,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAcnE"}
|
|
@@ -37,3 +37,18 @@ export function $createMentionNode(userID, isAtAll, displayText) {
|
|
|
37
37
|
export function $isMentionNode(node) {
|
|
38
38
|
return node instanceof MentionNode;
|
|
39
39
|
}
|
|
40
|
+
// Distinct specific users tagged so far — "Tất cả" (isAtAll) mentions aren't
|
|
41
|
+
// individual users and don't count toward the per-user cap.
|
|
42
|
+
export function $getMentionedUserIDs(root) {
|
|
43
|
+
const userIDs = new Set();
|
|
44
|
+
const walk = (node) => {
|
|
45
|
+
if ($isMentionNode(node) && !node.__isAtAll) {
|
|
46
|
+
userIDs.add(node.__userID);
|
|
47
|
+
}
|
|
48
|
+
if ("getChildren" in node && typeof node.getChildren === "function") {
|
|
49
|
+
node.getChildren().forEach(walk);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
walk(root);
|
|
53
|
+
return userIDs;
|
|
54
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMentionOptions.d.ts","sourceRoot":"","sources":["../../../../../src/components/message/footer/mention/useMentionOptions.ts"],"names":[],"mappings":"
|
|
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;AAW7C,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,GAAG,SAAS,EAC3B,OAAO,MAAM,KACZ,aAAa,EAyCf,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
3
|
import { useGroupMembers } from "../../../../hooks/group/useGroupMembers";
|
|
4
|
+
import useAuthStore from "../../../../store/auth";
|
|
4
5
|
export const ALL_MEMBERS_USER_ID = "__all__";
|
|
5
6
|
// Prefix matches rank above mid-string matches, then alphabetical — matches
|
|
6
7
|
// the "closest match first" ranking spec'd for this feature (not just a
|
|
@@ -14,11 +15,13 @@ const matchRank = (displayName, keyword) => {
|
|
|
14
15
|
export const useMentionOptions = (groupID, query) => {
|
|
15
16
|
const { t } = useTranslation();
|
|
16
17
|
const { members } = useGroupMembers(groupID);
|
|
18
|
+
const currentUserID = useAuthStore((state) => state.userID);
|
|
17
19
|
return useMemo(() => {
|
|
18
20
|
const keyword = query.trim().toLowerCase();
|
|
19
|
-
// Self-tagging
|
|
20
|
-
//
|
|
21
|
+
// Self-tagging isn't a useful mention target — the sender already
|
|
22
|
+
// knows who they are — so the current user is filtered out entirely.
|
|
21
23
|
const memberOptions = members
|
|
24
|
+
.filter((member) => member.userID !== currentUserID)
|
|
22
25
|
.map((member) => ({
|
|
23
26
|
userID: member.userID,
|
|
24
27
|
displayName: member.nickname || member.userID,
|
|
@@ -41,5 +44,5 @@ export const useMentionOptions = (groupID, query) => {
|
|
|
41
44
|
};
|
|
42
45
|
const matchesAll = !keyword || allOption.displayName.toLowerCase().includes(keyword);
|
|
43
46
|
return matchesAll ? [allOption, ...memberOptions] : memberOptions;
|
|
44
|
-
}, [members, query, t]);
|
|
47
|
+
}, [members, query, t, currentUserID]);
|
|
45
48
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { MessageItem } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
interface SessionEventMessageItemProps {
|
|
3
|
+
message: MessageItem;
|
|
4
|
+
}
|
|
5
|
+
declare const SessionEventMessageItem: ({ message }: SessionEventMessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
6
|
+
export default SessionEventMessageItem;
|
|
7
|
+
//# sourceMappingURL=SessionEventMessage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionEventMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/SessionEventMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,4BAA4B;IACpC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,QAAA,MAAM,uBAAuB,GAAI,aAAa,4BAA4B,mDAazE,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { getSessionEventText } from "../../../utils/messageLog";
|
|
5
|
+
const SessionEventMessageItem = ({ message }) => {
|
|
6
|
+
const { t } = useTranslation();
|
|
7
|
+
const text = getSessionEventText(message, t);
|
|
8
|
+
if (!text)
|
|
9
|
+
return null;
|
|
10
|
+
return (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "rounded-full bg-gray-200 px-3 py-1.5 text-center text-[13px] font-normal text-gray-900", children: text }) }));
|
|
11
|
+
};
|
|
12
|
+
export default SessionEventMessageItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AAwCtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA0BD,QAAA,MAAM,WAAW,GAAI,qMAalB,gBAAgB,mDA0TlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -20,7 +20,8 @@ import { useSendMessage, isMediaResendable, } from "../../../hooks/message/useSe
|
|
|
20
20
|
import { Icon } from "../../../components/icon";
|
|
21
21
|
import SystemLogMessageItem from "./SystemLogMessage";
|
|
22
22
|
import GroupNotificationMessageItem from "./GroupNotificationMessage";
|
|
23
|
-
import
|
|
23
|
+
import SessionEventMessageItem from "./SessionEventMessage";
|
|
24
|
+
import { isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType, } from "../../../utils/messageLog";
|
|
24
25
|
import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessageType, isPinnableMessageType, MessageToolboxActionKey, MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, useMessageToolboxVisibility, } from "../../../components/message/toolbox";
|
|
25
26
|
const MESSAGE_AVATAR_SIZE = 24;
|
|
26
27
|
const DIVIDER_GAP_MINUTES = 30;
|
|
@@ -41,7 +42,8 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
41
42
|
const contextMenuItems = useMemo(() => {
|
|
42
43
|
if ((message === null || message === void 0 ? void 0 : message.contentType) === MessageType.RevokeMessage ||
|
|
43
44
|
isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType) ||
|
|
44
|
-
isGroupNotificationMessageType(message === null || message === void 0 ? void 0 : message.contentType)
|
|
45
|
+
isGroupNotificationMessageType(message === null || message === void 0 ? void 0 : message.contentType) ||
|
|
46
|
+
isSessionEventMessage(message)) {
|
|
45
47
|
return [];
|
|
46
48
|
}
|
|
47
49
|
const closeToolbox = () => onContextMenuOpenChange === null || onContextMenuOpenChange === void 0 ? void 0 : onContextMenuOpenChange(false);
|
|
@@ -125,6 +127,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
125
127
|
const isInternalMessage = Boolean(message === null || message === void 0 ? void 0 : message.isInternal);
|
|
126
128
|
const isSystemLogMessage = isSystemLogMessageType(message === null || message === void 0 ? void 0 : message.contentType);
|
|
127
129
|
const isGroupNotificationMessage = isGroupNotificationMessageType(message === null || message === void 0 ? void 0 : message.contentType);
|
|
130
|
+
const isSessionEvent = isSessionEventMessage(message);
|
|
128
131
|
const isStickerMessage = (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.StickerMessage;
|
|
129
132
|
const hasContextMenu = contextMenuItems.length > 0;
|
|
130
133
|
const isLatestMine = ((_a = allMessages.find((m) => m.sendID === userID)) === null || _a === void 0 ? void 0 : _a.clientMsgID) ===
|
|
@@ -170,6 +173,11 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
170
173
|
timeBreakNode,
|
|
171
174
|
_jsx(GroupNotificationMessageItem, { message: message })));
|
|
172
175
|
}
|
|
176
|
+
if (isSessionEvent) {
|
|
177
|
+
return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
|
|
178
|
+
timeBreakNode,
|
|
179
|
+
_jsx(SessionEventMessageItem, { message: message })));
|
|
180
|
+
}
|
|
173
181
|
const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: renderBubbleContent(message, onPressQuoteMessage), isTransparent: isStickerMessage, timeLabel: dayjs.tz(message.sendTime).format("HH:mm") }));
|
|
174
182
|
const showPinnedBadge = Boolean(message === null || message === void 0 ? void 0 : message.isPinned);
|
|
175
183
|
const pinnedBadgeNode = showPinnedBadge ? (_jsx("span", { "data-testid": "message-pinned-badge", className: clsx("absolute -top-2.5 z-[1] flex h-[22px] w-[26px] items-center justify-center rounded-full border border-white bg-gray-25", isMine ? "left-0" : "right-0"), children: _jsx(Icon, { icon: "thumbtack-b", size: 12, className: "text-primary-400" }) })) : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/search-conversation/index.tsx"],"names":[],"mappings":"AAWA,oBAAY,wBAAwB;IAClC,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,QAAA,MAAM,kBAAkB,GAAI,gBAAgB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/search-conversation/index.tsx"],"names":[],"mappings":"AAWA,oBAAY,wBAAwB;IAClC,GAAG,QAAQ;IACX,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,QAAA,MAAM,kBAAkB,GAAI,gBAAgB,uBAAuB,4CA4ElE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { Tabs } from "antd";
|
|
4
|
-
import { useMemo, useState } from "react";
|
|
4
|
+
import { useEffect, useMemo, useState } from "react";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import SearchConversationAll from "./SearchAll";
|
|
7
7
|
import SearchConversationMyInbox from "./SearchConversationMyInbox";
|
|
@@ -48,6 +48,24 @@ const SearchConversation = ({ searchTerm }) => {
|
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
50
|
}, [t, searchTerm]);
|
|
51
|
-
|
|
51
|
+
// Clicking a result row while on "All" switches activeKey without any
|
|
52
|
+
// click/focus on the tab header itself — antd's own scroll-into-view for
|
|
53
|
+
// that path relies on measuring .ant-tabs-nav against its scroll parent,
|
|
54
|
+
// which the fixed-width nav below permanently reports as "already fits"
|
|
55
|
+
// (see the renderTabBar comment). Do it ourselves instead.
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
var _a;
|
|
58
|
+
(_a = document
|
|
59
|
+
.querySelector('[data-cp-name="SearchConversation"] .ant-tabs-tab-active')) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ inline: "nearest", block: "nearest" });
|
|
60
|
+
}, [activeKey]);
|
|
61
|
+
return (_jsx("div", { className: "h-full", "data-cp-name": "SearchConversation", children: _jsx(Tabs, { defaultActiveKey: SearchConversationTabKey.All, items: items, className: "h-full", activeKey: activeKey, onChange: (key) => setActiveKey(key), destroyOnHidden: true,
|
|
62
|
+
// Ant's built-in tab-overflow ("...") dropdown never recovers from
|
|
63
|
+
// its initial off-screen measurement inside this Drawer (rendered
|
|
64
|
+
// with getContainer={false} for unrelated scroll-position reasons —
|
|
65
|
+
// see DeskConversationList) — the last tab (Nhóm) becomes
|
|
66
|
+
// permanently unreachable. A plain horizontally-scrollable strip
|
|
67
|
+
// sidesteps that positioning bug entirely: every tab stays a real,
|
|
68
|
+
// always-clickable element: no hidden dropdown involved.
|
|
69
|
+
renderTabBar: (tabBarProps, DefaultTabBar) => (_jsx("div", { className: "overflow-x-auto", children: _jsx(DefaultTabBar, Object.assign({}, tabBarProps, { className: "!w-max" })) })) }) }));
|
|
52
70
|
};
|
|
53
71
|
export default SearchConversation;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchItemAsGroup.d.ts","sourceRoot":"","sources":["../../../../src/components/search-conversation/item/SearchItemAsGroup.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAOxF,UAAU,sBAAsB;IAC9B,KAAK,EAAE,4BAA4B,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,iBAAiB,GAAI,OAAO,sBAAsB,
|
|
1
|
+
{"version":3,"file":"SearchItemAsGroup.d.ts","sourceRoot":"","sources":["../../../../src/components/search-conversation/item/SearchItemAsGroup.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAOxF,UAAU,sBAAsB;IAC9B,KAAK,EAAE,4BAA4B,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,iBAAiB,GAAI,OAAO,sBAAsB,4CAiEvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -16,7 +16,8 @@ const SearchItemAsGroup = (props) => {
|
|
|
16
16
|
const { conversation } = group;
|
|
17
17
|
const isPublic = group.visibility === GroupVisibility.Public;
|
|
18
18
|
const onPressItem = () => {
|
|
19
|
-
|
|
19
|
+
// Result can go stale before the click — force a fresh membership check.
|
|
20
|
+
setConversationData(conversation, undefined, { forceRevalidate: true });
|
|
20
21
|
setSelectedConversationId(conversation.conversationID);
|
|
21
22
|
const newSearchParams = new URLSearchParams(window.location.search);
|
|
22
23
|
newSearchParams.set("threadId", conversation.conversationID);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchItemAsMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/search-conversation/item/SearchItemAsMessage.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAA4B,MAAM,8BAA8B,CAAC;AAerF,UAAU,wBAAwB;IAChC,OAAO,EAAE,WAAW,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,mBAAmB,GAAI,OAAO,wBAAwB,
|
|
1
|
+
{"version":3,"file":"SearchItemAsMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/search-conversation/item/SearchItemAsMessage.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAA4B,MAAM,8BAA8B,CAAC;AAerF,UAAU,wBAAwB;IAChC,OAAO,EAAE,WAAW,GAAG;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,QAAA,MAAM,mBAAmB,GAAI,OAAO,wBAAwB,4CA2G3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -36,7 +36,10 @@ const SearchItemAsMessage = (props) => {
|
|
|
36
36
|
}
|
|
37
37
|
useConversationStore
|
|
38
38
|
.getState()
|
|
39
|
-
|
|
39
|
+
// Result can be stale — force a fresh membership check.
|
|
40
|
+
.setConversationData(data, message.clientMsgID, {
|
|
41
|
+
forceRevalidate: true,
|
|
42
|
+
});
|
|
40
43
|
useConversationStore
|
|
41
44
|
.getState()
|
|
42
45
|
.setSelectedConversationId(data.conversationID);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAaA,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;CAChB;AAqED,eAAO,MAAM,sBAAsB,GAAI,6CAKpC,4BAA4B;;;
|
|
1
|
+
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAaA,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;CAChB;AAqED,eAAO,MAAM,sBAAsB,GAAI,6CAKpC,4BAA4B;;;CA2E9B,CAAC"}
|