@droppii-org/chat-sdk 0.4.10 → 0.4.12
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-detail/ConversationDetailInput.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationDetailInput.js +35 -1
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +5 -1
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +5 -1
- package/dist/components/message/footer/index.d.ts.map +1 -1
- package/dist/components/message/footer/index.js +35 -2
- package/dist/components/message/item/MessageBubble.d.ts +5 -1
- package/dist/components/message/item/MessageBubble.d.ts.map +1 -1
- package/dist/components/message/item/MessageBubble.js +4 -6
- package/dist/components/message/item/index.d.ts +5 -1
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +23 -7
- package/dist/hooks/message/useMessage.d.ts +1 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +11 -0
- package/dist/locales/vi/conversation-inbox.json +1 -1
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +6 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConversationDetailInput.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationDetailInput.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;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,4CA8qB9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -4,8 +4,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import { useBoolean } from "ahooks";
|
|
6
6
|
import clsx from "clsx";
|
|
7
|
+
import { Button, message as toastMessage } from "antd";
|
|
7
8
|
import { Icon } from "../../components/icon";
|
|
8
9
|
import { useSendMessage } from "../../hooks/message/useSendMessage";
|
|
10
|
+
import { useJoinPublicGroup } from "../../hooks/group/useJoinPublicGroup";
|
|
9
11
|
import useAuthStore from "../../store/auth";
|
|
10
12
|
import useConversationStore from "../../store/conversation";
|
|
11
13
|
import useMessageDraftStore from "../../store/messageDraft";
|
|
@@ -36,6 +38,38 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
|
|
|
36
38
|
const conversationId = selectedConversationId || (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
|
|
37
39
|
const isConversationReady = !!conversationData && (!!conversationData.userID || !!conversationData.groupID);
|
|
38
40
|
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
41
|
+
const setIsViewOnlyMember = useConversationStore((state) => state.setIsViewOnlyMember);
|
|
42
|
+
const getCurrentMemberInGroupByReq = useConversationStore((state) => state.getCurrentMemberInGroupByReq);
|
|
43
|
+
const getSelfGroupPermissionByReq = useConversationStore((state) => state.getSelfGroupPermissionByReq);
|
|
44
|
+
const { mutate: joinPublicGroup, isPending: isJoiningPublicGroup } = useJoinPublicGroup();
|
|
45
|
+
const handleJoinPublicGroup = useCallback(() => {
|
|
46
|
+
const groupID = conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID;
|
|
47
|
+
if (!groupID)
|
|
48
|
+
return;
|
|
49
|
+
joinPublicGroup({ groupID }, {
|
|
50
|
+
onSuccess: async () => {
|
|
51
|
+
toastMessage.success(tCommon("join_group_success"));
|
|
52
|
+
// updateCurrentConversation's revalidation only runs on a
|
|
53
|
+
// conversationID change — this is the same conversation the user
|
|
54
|
+
// was already viewing, so refresh membership/permissions directly
|
|
55
|
+
// instead, and flip isViewOnlyMember off immediately rather than
|
|
56
|
+
// waiting on a WS event that may not fire right away.
|
|
57
|
+
setIsViewOnlyMember(false);
|
|
58
|
+
await Promise.allSettled([
|
|
59
|
+
getCurrentMemberInGroupByReq(groupID),
|
|
60
|
+
getSelfGroupPermissionByReq(groupID),
|
|
61
|
+
]);
|
|
62
|
+
},
|
|
63
|
+
onError: () => toastMessage.error(tCommon("join_group_failed")),
|
|
64
|
+
});
|
|
65
|
+
}, [
|
|
66
|
+
conversationData,
|
|
67
|
+
getCurrentMemberInGroupByReq,
|
|
68
|
+
getSelfGroupPermissionByReq,
|
|
69
|
+
joinPublicGroup,
|
|
70
|
+
setIsViewOnlyMember,
|
|
71
|
+
tCommon,
|
|
72
|
+
]);
|
|
39
73
|
const sendMessageOptions = useMemo(() => ({
|
|
40
74
|
scrollEvent: "CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM",
|
|
41
75
|
}), []);
|
|
@@ -408,7 +442,7 @@ const ConversationDetailInput = ({ placeholder, onSendMessage, onInputChange, mo
|
|
|
408
442
|
void handleSend();
|
|
409
443
|
};
|
|
410
444
|
if (isViewOnlyMember) {
|
|
411
|
-
return (
|
|
445
|
+
return (_jsxs("div", { "data-testid": "conversation-detail-input-view-only", className: "flex w-full shrink-0 items-center justify-between gap-3 bg-chat-bubble-neutral/[0.05] px-4 py-3", children: [_jsx("span", { className: "text-sm text-gray-500", children: t("thread_detail.view_only_notice") }), _jsx(Button, { type: "primary", size: "small", loading: isJoiningPublicGroup, onClick: handleJoinPublicGroup, children: tCommon("join_group") })] }));
|
|
412
446
|
}
|
|
413
447
|
return (_jsxs("div", { ref: containerRef, "data-testid": "conversation-detail-input", "data-cp-name": "ConversationDetailInput", className: "flex w-full shrink-0 flex-col bg-chat-bubble-neutral/[0.05]", children: [_jsx("input", { ref: mediaInputRef, type: "file", accept: "image/jpeg,image/png,image/jpg,video/*", multiple: true, className: "hidden", onChange: handleMediaInputChange }), _jsx("input", { ref: documentInputRef, type: "file", accept: ".pdf,.doc,.docx", multiple: true, className: "hidden", onChange: handleDocumentInputChange }), _jsx(ReplyPreviewBar, {}), listUploadFiles.length > 0 && (_jsx(FilePreviewList, { files: listUploadFiles, onRemove: (file) => handleRemoveFile(file.uid), onVideoReady: () => setIsVideoLoading(false) })), showLinkPreview && previewUrl && (_jsx(LinkInputPreview, { url: previewUrl, onRemove: () => setPreviewDismissed(true) })), _jsxs("div", { className: "flex items-end gap-3 px-3 py-2", children: [_jsx("div", { className: "flex shrink-0 items-center", children: _jsx("button", { type: "button", onClick: handleToggleMoreOption, "aria-label": t("thread_detail.more_option.toggle"), "aria-expanded": isMoreOptionOpen, "data-testid": "conversation-detail-more-option-toggle", className: "flex py-3 items-center justify-center rounded-full", children: isMoreOptionOpen ? (_jsx(Icon, { icon: "close-circle-b", size: 24, className: "text-primary-400" })) : (_jsx(Icon, { icon: "plus-circle-o", size: 24, className: "text-gray-900" })) }) }), _jsx("div", { className: clsx("flex min-w-0 flex-1 overflow-hidden rounded-xl border bg-white transition-shadow", isFocused
|
|
414
448
|
? "border-primary-200 shadow-primary-focus"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,gBAAgB,4BAA4B,
|
|
1
|
+
{"version":3,"file":"ConversationMessageList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ConversationMessageList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAmBzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,QAAA,MAAM,uBAAuB,GAAI,gBAAgB,4BAA4B,4CAoM5E,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -37,6 +37,10 @@ const ConversationMessageList = ({ bottomSlot }) => {
|
|
|
37
37
|
const isMessageLoading = !conversationId || loadState.initLoading;
|
|
38
38
|
const [openToolboxMessageId, setOpenToolboxMessageId] = useState(null);
|
|
39
39
|
const closeToolbox = useCallback(() => setOpenToolboxMessageId(null), []);
|
|
40
|
+
const [revealedTimeMsgId, setRevealedTimeMsgId] = useState(null);
|
|
41
|
+
const handleToggleTimeReveal = useCallback((clientMsgID) => {
|
|
42
|
+
setRevealedTimeMsgId((prev) => (prev === clientMsgID ? null : clientMsgID));
|
|
43
|
+
}, []);
|
|
40
44
|
const { pinMessage, unpinMessage } = usePinMessage();
|
|
41
45
|
const pinnedTotalCount = useConversationStore((state) => state.pinnedTotalCount);
|
|
42
46
|
const pinnedRemainingCount = useConversationStore((state) => state.pinnedRemainingCount);
|
|
@@ -95,6 +99,6 @@ const ConversationMessageList = ({ bottomSlot }) => {
|
|
|
95
99
|
target: scrollRef,
|
|
96
100
|
enable: !!openToolboxMessageId,
|
|
97
101
|
});
|
|
98
|
-
return (_jsxs("div", { "data-testid": "conversation-message-list", "data-cp-name": "ConversationMessageList", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: [isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [bottomSlot, loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: setQuotedMessage, onRevokeMessage: handleRevokeMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, enableLongPress: true }, message.clientMsgID)))] })), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
|
|
102
|
+
return (_jsxs("div", { "data-testid": "conversation-message-list", "data-cp-name": "ConversationMessageList", className: "relative flex min-h-0 min-w-0 flex-1 flex-col overflow-x-hidden bg-white", children: [isMessageLoading ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Spin, {}) })) : (_jsxs(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableConversationMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, containerClassName: "h-full", empty: _jsx("div", { className: "flex flex-1 items-center justify-center py-12", children: _jsx(Empty, { description: t("empty.no_messages") }) }), children: [bottomSlot, loadState.messageList.map((message, index, array) => (_jsx(MessageItem, { message: message, allMessages: array, isGroupChat: isGroupChat, forceHideTip: index === 0 && !!bottomSlot, contextMenuOpen: openToolboxMessageId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenToolboxMessageId(open ? message.clientMsgID : null), onQuoteMessage: setQuotedMessage, onRevokeMessage: handleRevokeMessage, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined, enableLongPress: true }, message.clientMsgID)))] })), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
|
|
99
103
|
};
|
|
100
104
|
export default ConversationMessageList;
|
|
@@ -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,4CAiU3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -61,6 +61,10 @@ const MessageList = (props) => {
|
|
|
61
61
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
62
62
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
63
63
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
64
|
+
const [revealedTimeMsgId, setRevealedTimeMsgId] = useState(null);
|
|
65
|
+
const handleToggleTimeReveal = useCallback((clientMsgID) => {
|
|
66
|
+
setRevealedTimeMsgId((prev) => (prev === clientMsgID ? null : clientMsgID));
|
|
67
|
+
}, []);
|
|
64
68
|
const [showConfirmRevoke, { setTrue: openConfirmRevoke, setFalse: closeConfirmRevoke },] = useBoolean(false);
|
|
65
69
|
const { revokeMessage, loading: isRevoking } = useRevokeMessage();
|
|
66
70
|
const { pinMessage, unpinMessage } = usePinMessage();
|
|
@@ -160,7 +164,7 @@ const MessageList = (props) => {
|
|
|
160
164
|
if (!conversationData) {
|
|
161
165
|
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
166
|
}
|
|
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 ? (
|
|
167
|
+
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, timeRevealed: revealedTimeMsgId === message.clientMsgID, onToggleTimeReveal: handleToggleTimeReveal }, 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 })) : !isGroupChat && 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
168
|
// Brief fallback while isJoined catches up after the kick event.
|
|
165
169
|
_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) })] }));
|
|
166
170
|
};
|
|
@@ -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;
|
|
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;AAUtB,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,4CAgYpB,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -10,7 +10,8 @@ import EnterHandler from "./EnterHandler";
|
|
|
10
10
|
import ActionBar from "./ActionBar";
|
|
11
11
|
import { AccountType, } from "../../../types/chat";
|
|
12
12
|
import { useSendMessage } from "../../../hooks/message/useSendMessage";
|
|
13
|
-
import {
|
|
13
|
+
import { useJoinPublicGroup } from "../../../hooks/group/useJoinPublicGroup";
|
|
14
|
+
import { Button, Modal, Popover, message as toastMessage } from "antd";
|
|
14
15
|
import FilePreview from "./FilePreview";
|
|
15
16
|
import LinkInputPreview from "./LinkInputPreview";
|
|
16
17
|
import ComposerTextSyncPlugin from "./ComposerTextSyncPlugin";
|
|
@@ -72,6 +73,38 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
|
|
|
72
73
|
const quotedMessage = useConversationStore((state) => state.quotedMessage);
|
|
73
74
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
74
75
|
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
76
|
+
const setIsViewOnlyMember = useConversationStore((state) => state.setIsViewOnlyMember);
|
|
77
|
+
const getCurrentMemberInGroupByReq = useConversationStore((state) => state.getCurrentMemberInGroupByReq);
|
|
78
|
+
const getSelfGroupPermissionByReq = useConversationStore((state) => state.getSelfGroupPermissionByReq);
|
|
79
|
+
const { mutate: joinPublicGroup, isPending: isJoiningPublicGroup } = useJoinPublicGroup();
|
|
80
|
+
const handleJoinPublicGroup = useCallback(() => {
|
|
81
|
+
const groupID = conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID;
|
|
82
|
+
if (!groupID)
|
|
83
|
+
return;
|
|
84
|
+
joinPublicGroup({ groupID }, {
|
|
85
|
+
onSuccess: async () => {
|
|
86
|
+
toastMessage.success(t("join_group_success"));
|
|
87
|
+
// updateCurrentConversation's revalidation only runs on a
|
|
88
|
+
// conversationID change — this is the same conversation the user
|
|
89
|
+
// was already viewing, so refresh membership/permissions directly
|
|
90
|
+
// instead, and flip isViewOnlyMember off immediately rather than
|
|
91
|
+
// waiting on a WS event that may not fire right away.
|
|
92
|
+
setIsViewOnlyMember(false);
|
|
93
|
+
await Promise.allSettled([
|
|
94
|
+
getCurrentMemberInGroupByReq(groupID),
|
|
95
|
+
getSelfGroupPermissionByReq(groupID),
|
|
96
|
+
]);
|
|
97
|
+
},
|
|
98
|
+
onError: () => toastMessage.error(t("join_group_failed")),
|
|
99
|
+
});
|
|
100
|
+
}, [
|
|
101
|
+
conversationData,
|
|
102
|
+
getCurrentMemberInGroupByReq,
|
|
103
|
+
getSelfGroupPermissionByReq,
|
|
104
|
+
joinPublicGroup,
|
|
105
|
+
setIsViewOnlyMember,
|
|
106
|
+
t,
|
|
107
|
+
]);
|
|
75
108
|
// Internal notes are a CX-support concept (team-only remarks not sent to
|
|
76
109
|
// the customer) — meaningless inside a real member-managed group, where
|
|
77
110
|
// everyone in the thread already is the "team".
|
|
@@ -216,7 +249,7 @@ const MessageFooterProvider = ({ currentSession, openCreateCannedModal, }) => {
|
|
|
216
249
|
closeCanned();
|
|
217
250
|
};
|
|
218
251
|
if (isViewOnlyMember) {
|
|
219
|
-
return (
|
|
252
|
+
return (_jsxs("div", { "data-testid": "message-footer-view-only", className: "flex w-full items-center justify-between gap-3 border-t border-gray-25 bg-white px-4 py-3", children: [_jsx("span", { className: "text-sm text-gray-500", children: tConversationInbox("thread_detail.view_only_notice") }), _jsx(Button, { type: "primary", size: "small", loading: isJoiningPublicGroup, onClick: handleJoinPublicGroup, children: t("join_group") })] }));
|
|
220
253
|
}
|
|
221
254
|
return (_jsxs(MessageFooterContext.Provider, { value: {
|
|
222
255
|
onSendMessage,
|
|
@@ -9,7 +9,11 @@ export interface MessageBubbleProps {
|
|
|
9
9
|
id?: string;
|
|
10
10
|
isTransparent?: boolean;
|
|
11
11
|
timeLabel?: string;
|
|
12
|
+
/** Whether this specific message's time label is currently revealed by tap. */
|
|
13
|
+
timeRevealed?: boolean;
|
|
14
|
+
/** Called on bubble tap to toggle this message as the single revealed one. */
|
|
15
|
+
onToggleTimeReveal?: () => void;
|
|
12
16
|
}
|
|
13
|
-
declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, timeLabel, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, timeLabel, timeRevealed, onToggleTimeReveal, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
18
|
export default MessageBubble;
|
|
15
19
|
//# sourceMappingURL=MessageBubble.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;CACjC;AA6CD,QAAA,MAAM,aAAa,GAAI,uIAYpB,kBAAkB,4CAiEpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
-
import { useState } from "react";
|
|
5
4
|
const INCOMING_TAIL_PATH = "M1.3798 18H4V0C3.51762 4.5 3.34269 10.5 0.966518 14C-0.568384 16.2608 -0.159364 18 1.3798 18Z";
|
|
6
5
|
const OUTGOING_TAIL_PATH = "M2.6202 18H0V0C0.482375 4.5 0.657308 10.5 3.03348 14C4.56838 16.2608 4.15936 18 2.6202 18Z";
|
|
7
6
|
const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className: clsx("flex shrink-0 items-center justify-center", isInternal
|
|
@@ -9,10 +8,9 @@ const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className:
|
|
|
9
8
|
: isMine
|
|
10
9
|
? "text-chat-bubble-mine"
|
|
11
10
|
: "text-chat-bubble-neutral"), "data-cp-name": "MessageBubbleTail", children: _jsx("div", { className: "flex-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "4", height: "18", viewBox: "0 0 4 18", fill: "none", "aria-hidden": true, children: _jsx("path", { d: isMine ? OUTGOING_TAIL_PATH : INCOMING_TAIL_PATH, fill: "currentColor", fillOpacity: "0.1" }) }) }) }));
|
|
12
|
-
const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, timeLabel, }) => {
|
|
11
|
+
const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, timeLabel, timeRevealed = false, onToggleTimeReveal, }) => {
|
|
13
12
|
const showTail = showTip && !isTransparent;
|
|
14
|
-
const
|
|
15
|
-
const showTimeLabel = Boolean(timeLabel) && (showTip || timeRevealed);
|
|
13
|
+
const showTimeLabel = Boolean(timeLabel) && timeRevealed;
|
|
16
14
|
const bubbleClass = clsx("relative min-w-0 max-w-full", isTransparent
|
|
17
15
|
? "bg-transparent p-0"
|
|
18
16
|
: clsx("p-2", showTip
|
|
@@ -25,8 +23,8 @@ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip =
|
|
|
25
23
|
? "bg-chat-bubble-mine/10"
|
|
26
24
|
: "bg-chat-bubble-neutral/10"));
|
|
27
25
|
return (_jsxs("div", { className: "relative flex min-w-0 max-w-full items-end", "data-testid": "message-bubble", "data-cp-name": "MessageBubble", onClick: () => {
|
|
28
|
-
if (
|
|
29
|
-
|
|
26
|
+
if (timeLabel)
|
|
27
|
+
onToggleTimeReveal === null || onToggleTimeReveal === void 0 ? void 0 : onToggleTimeReveal();
|
|
30
28
|
}, children: [!isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), _jsx("div", { className: bubbleClass, id: id, children: _jsxs("div", { className: clsx("flex flex-col gap-1", !isTransparent && "px-1"), children: [showSenderName && senderName && (_jsx("span", { className: "text-[12px] font-medium leading-[160%] tracking-[0.12px] text-gray-400", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), showTimeLabel && (_jsx("span", { "data-testid": "message-time-label", className: clsx("absolute -bottom-2 flex h-[19px] w-[30px] items-center justify-center rounded-full bg-white text-[10px] text-gray-400 shadow-sm", isMine ? "right-1" : "left-1"), children: timeLabel }))] }));
|
|
31
29
|
};
|
|
32
30
|
export default MessageBubble;
|
|
@@ -12,7 +12,11 @@ interface MessageItemProps {
|
|
|
12
12
|
onPinMessage?: (message: MessageItemType) => void;
|
|
13
13
|
onUnpinMessage?: (message: MessageItemType) => void;
|
|
14
14
|
enableLongPress?: boolean;
|
|
15
|
+
/** Whether this message is the single one with its time label revealed. */
|
|
16
|
+
timeRevealed?: boolean;
|
|
17
|
+
/** Called on bubble tap to toggle this message as the revealed one. */
|
|
18
|
+
onToggleTimeReveal?: (clientMsgID: string) => void;
|
|
15
19
|
}
|
|
16
|
-
declare const MessageItem: ({ message, allMessages, isGroupChat, forceHideTip, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, onPinMessage, onUnpinMessage, enableLongPress, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
20
|
+
declare const MessageItem: ({ message, allMessages, isGroupChat, forceHideTip, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, onPinMessage, onUnpinMessage, enableLongPress, timeRevealed, onToggleTimeReveal, }: MessageItemProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
17
21
|
export default MessageItem;
|
|
18
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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;AAyCtC,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;IAC1B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AA2BD,QAAA,MAAM,WAAW,GAAI,uOAelB,gBAAgB,mDA4UlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -6,7 +6,7 @@ import { Avatar, Dropdown, message as toastMessage } from "antd";
|
|
|
6
6
|
import { MessageStatus, MessageType, } from "@droppii-org/wasm-client-sdk";
|
|
7
7
|
import MessageBubble from "./MessageBubble";
|
|
8
8
|
import { renderBubbleContent } from "./renderMessageContent";
|
|
9
|
-
import { getVisibleNeighbor, isMessageHiddenForViewer, } from "../../../hooks/message/useMessage";
|
|
9
|
+
import { getImmediateNeighbor, getVisibleNeighbor, isMessageHiddenForViewer, } from "../../../hooks/message/useMessage";
|
|
10
10
|
import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../../constants";
|
|
11
11
|
import { formatDividerTimestamp } from "../../../utils/common";
|
|
12
12
|
import { CustomMessageType } from "../../../types/customMessage";
|
|
@@ -26,12 +26,13 @@ import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessage
|
|
|
26
26
|
const MESSAGE_AVATAR_SIZE = 24;
|
|
27
27
|
const DIVIDER_GAP_MINUTES = 30;
|
|
28
28
|
const GROUPING_GAP_MINUTES = 5;
|
|
29
|
+
const GROUPING_GAP_MS = GROUPING_GAP_MINUTES * 60 * 1000;
|
|
29
30
|
const isOrderMessage = (message) => { var _a; return ((_a = message === null || message === void 0 ? void 0 : message.customElem) === null || _a === void 0 ? void 0 : _a.extension) === CustomMessageType.Order; };
|
|
30
31
|
const MessageAvatar = ({ avatarUrl, senderNickname, }) => {
|
|
31
32
|
var _a;
|
|
32
33
|
return (_jsx(Avatar, { size: MESSAGE_AVATAR_SIZE, src: avatarUrl, className: "shrink-0 self-end", "data-cp-name": "MessageAvatar", children: ((_a = senderNickname === null || senderNickname === void 0 ? void 0 : senderNickname.charAt) === null || _a === void 0 ? void 0 : _a.call(senderNickname, 0)) || "A" }));
|
|
33
34
|
};
|
|
34
|
-
const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip = false, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, onPinMessage, onUnpinMessage, enableLongPress = false, }) => {
|
|
35
|
+
const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip = false, contextMenuOpen, onContextMenuOpenChange, onRevokeMessage, onQuoteMessage, onPressQuoteMessage, onPinMessage, onUnpinMessage, enableLongPress = false, timeRevealed = false, onToggleTimeReveal, }) => {
|
|
35
36
|
var _a;
|
|
36
37
|
const { t } = useTranslation();
|
|
37
38
|
const { resendMessage } = useSendMessage();
|
|
@@ -135,18 +136,33 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
135
136
|
const previousMessage = getVisibleNeighbor(allMessages, message, "prev");
|
|
136
137
|
const nextMessage = getVisibleNeighbor(allMessages, message, "next");
|
|
137
138
|
const isOrder = isOrderMessage(message);
|
|
139
|
+
// A system-log or session-event item breaks the group even though it's
|
|
140
|
+
// skipped by getVisibleNeighbor's "next ordinary message" lookup — so
|
|
141
|
+
// check the *immediate* array neighbor here, not the nearest ordinary one.
|
|
142
|
+
const immediatePrev = getImmediateNeighbor(allMessages, message, "prev");
|
|
143
|
+
const immediateNext = getImmediateNeighbor(allMessages, message, "next");
|
|
144
|
+
const prevBreaksGroup = immediatePrev
|
|
145
|
+
? isSystemLogMessageType(immediatePrev.contentType) ||
|
|
146
|
+
isSessionEventMessage(immediatePrev)
|
|
147
|
+
: false;
|
|
148
|
+
const nextBreaksGroup = immediateNext
|
|
149
|
+
? isSystemLogMessageType(immediateNext.contentType) ||
|
|
150
|
+
isSessionEventMessage(immediateNext)
|
|
151
|
+
: false;
|
|
138
152
|
const prevSameUser = !isOrder &&
|
|
153
|
+
!prevBreaksGroup &&
|
|
139
154
|
!!previousMessage &&
|
|
140
155
|
!isOrderMessage(previousMessage) &&
|
|
141
156
|
previousMessage.sendID === (message === null || message === void 0 ? void 0 : message.sendID) &&
|
|
142
|
-
Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "
|
|
143
|
-
|
|
157
|
+
Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "millisecond")) <=
|
|
158
|
+
GROUPING_GAP_MS;
|
|
144
159
|
const nextSameUser = !isOrder &&
|
|
160
|
+
!nextBreaksGroup &&
|
|
145
161
|
!!nextMessage &&
|
|
146
162
|
!isOrderMessage(nextMessage) &&
|
|
147
163
|
nextMessage.sendID === (message === null || message === void 0 ? void 0 : message.sendID) &&
|
|
148
|
-
Math.abs(dayjs(nextMessage.sendTime).diff(message.sendTime, "
|
|
149
|
-
|
|
164
|
+
Math.abs(dayjs(nextMessage.sendTime).diff(message.sendTime, "millisecond")) <=
|
|
165
|
+
GROUPING_GAP_MS;
|
|
150
166
|
const showTimeBreak = !previousMessage ||
|
|
151
167
|
!dayjs(message.sendTime).isSame(previousMessage.sendTime, "days") ||
|
|
152
168
|
Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "minute")) >
|
|
@@ -178,7 +194,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
178
194
|
timeBreakNode,
|
|
179
195
|
_jsx(SessionEventMessageItem, { message: message })));
|
|
180
196
|
}
|
|
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") }));
|
|
197
|
+
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"), timeRevealed: timeRevealed, onToggleTimeReveal: () => onToggleTimeReveal === null || onToggleTimeReveal === void 0 ? void 0 : onToggleTimeReveal(message.clientMsgID) }));
|
|
182
198
|
const showPinnedBadge = Boolean(message === null || message === void 0 ? void 0 : message.isPinned);
|
|
183
199
|
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;
|
|
184
200
|
const wrappedBubble = hasContextMenu ? (_jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: ["contextMenu"], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, overlayClassName: MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, children: _jsxs("div", { ref: bubbleWrapperRef, className: "relative min-w-0 max-w-full", children: [bubbleNode, pinnedBadgeNode] }) })) : (_jsxs("div", { className: "relative min-w-0 max-w-full", children: [bubbleNode, pinnedBadgeNode] }));
|
|
@@ -26,4 +26,5 @@ export declare const pushNewMessage: (message: MessageItem) => void;
|
|
|
26
26
|
export declare const updateOneMessage: (message: MessageItem) => void;
|
|
27
27
|
export declare const removeOneMessage: (clientMsgID: string) => void;
|
|
28
28
|
export declare const getVisibleNeighbor: (allMessages: MessageItem[], current: MessageItem, direction: "prev" | "next") => MessageItem | undefined;
|
|
29
|
+
export declare const getImmediateNeighbor: (allMessages: MessageItem[], current: MessageItem, direction: "prev" | "next") => MessageItem | undefined;
|
|
29
30
|
//# sourceMappingURL=useMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAatC,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,CAAA;CAAE,KAC9D,OAqBF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB;;;;;qBAeA,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;CAsSnC,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,KACzB,WAAW,GAAG,SAkBhB,CAAC"}
|
|
1
|
+
{"version":3,"file":"useMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useMessage.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,WAAW,EACX,WAAW,EAEZ,MAAM,8BAA8B,CAAC;AAatC,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,CAAA;CAAE,KAC9D,OAqBF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB;;;;;qBAeA,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;CAsSnC,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,KACzB,WAAW,GAAG,SAkBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC"}
|
|
@@ -303,6 +303,17 @@ export const getVisibleNeighbor = (allMessages, current, direction) => {
|
|
|
303
303
|
}
|
|
304
304
|
return undefined;
|
|
305
305
|
};
|
|
306
|
+
// Unlike getVisibleNeighbor, this does not skip past system-log/session-event
|
|
307
|
+
// items — used to detect when one of those sits directly next to a message,
|
|
308
|
+
// since such an item must end the current sender's group even though it is
|
|
309
|
+
// not itself a group-breaking "different sender" message.
|
|
310
|
+
export const getImmediateNeighbor = (allMessages, current, direction) => {
|
|
311
|
+
const currentIndex = allMessages.findIndex((m) => m.clientMsgID === current.clientMsgID);
|
|
312
|
+
if (currentIndex === -1)
|
|
313
|
+
return undefined;
|
|
314
|
+
const step = direction === "prev" ? 1 : -1;
|
|
315
|
+
return allMessages[currentIndex + step];
|
|
316
|
+
};
|
|
306
317
|
const removeDuplicateMessages = (messages) => {
|
|
307
318
|
const seen = new Set();
|
|
308
319
|
return messages.filter((msg) => {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"profile_hint": "Nhấn để xem thông tin",
|
|
73
73
|
"options_title": "Tuỳ chọn",
|
|
74
74
|
"input_placeholder": "Nhập tin nhắn...",
|
|
75
|
-
"view_only_notice": "Bạn
|
|
75
|
+
"view_only_notice": "Bạn cần tham gia nhóm để gửi tin nhắn",
|
|
76
76
|
"resolve_failed": "Không thể mở cuộc trò chuyện",
|
|
77
77
|
"more_option": {
|
|
78
78
|
"toggle": "Tùy chọn khác",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAO3C,QAAA,MAAM,oBAAoB,gFAqXvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
|
|
@@ -2,6 +2,7 @@ import { GroupVisibility, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
|
2
2
|
import { create } from "zustand";
|
|
3
3
|
import { DChatSDK } from "../constants/sdk";
|
|
4
4
|
import { conversationSort, isGroupConversation } from "../utils/imCommon";
|
|
5
|
+
import { PeerType } from "../types/conversation";
|
|
5
6
|
import useUserStore from "./user";
|
|
6
7
|
const CONVERSATION_SPLIT_COUNT = 500;
|
|
7
8
|
const useConversationStore = create((set, get) => ({
|
|
@@ -133,13 +134,16 @@ const useConversationStore = create((set, get) => ({
|
|
|
133
134
|
// derive the same public->view-only / private->block state from the
|
|
134
135
|
// fetch/cache above. currentConversation isn't set until after this
|
|
135
136
|
// block, so guard via conversationData's groupID instead.
|
|
137
|
+
// Require confirmed peerType — a CX/bot session can ride a Group
|
|
138
|
+
// session with peerType missing from a raw fetch; don't block on that.
|
|
139
|
+
const isConfirmedGroup = conversation.peerType === PeerType.GROUP;
|
|
136
140
|
const stillOpen = ((_a = get().conversationData) === null || _a === void 0 ? void 0 : _a.groupID) === groupID;
|
|
137
141
|
const notAMember = !get().currentMemberInGroup.has(groupID);
|
|
138
142
|
const isPublic = ((_b = get().currentGroupInfo.get(groupID)) === null || _b === void 0 ? void 0 : _b.visibility) === GroupVisibility.Public;
|
|
139
|
-
if (stillOpen && notAMember && isPublic) {
|
|
143
|
+
if (isConfirmedGroup && stillOpen && notAMember && isPublic) {
|
|
140
144
|
set({ isViewOnlyMember: true });
|
|
141
145
|
}
|
|
142
|
-
else if (stillOpen && notAMember) {
|
|
146
|
+
else if (isConfirmedGroup && stillOpen && notAMember) {
|
|
143
147
|
get().clearSelectedConversation();
|
|
144
148
|
get().showRemovedFromGroupModal();
|
|
145
149
|
}
|