@droppii-org/chat-sdk 0.6.0 → 0.6.2
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 +15 -5
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +27 -11
- package/dist/components/message/item/AtTextMessage.d.ts.map +1 -1
- package/dist/components/message/item/AtTextMessage.js +25 -13
- package/dist/components/message/item/QuoteMessage.js +2 -2
- package/dist/components/message/item/renderMessageContent.d.ts.map +1 -1
- package/dist/components/message/item/renderMessageContent.js +6 -6
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +7 -2
- package/dist/hooks/message/useSendMessage.d.ts +1 -1
- package/dist/hooks/message/useSendMessage.d.ts.map +1 -1
- package/dist/hooks/message/useSendMessage.js +12 -7
- package/dist/hooks/session/useConversationSessionState.d.ts +2 -1
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
- package/dist/hooks/session/useConversationSessionState.js +14 -5
- package/dist/hooks/session/useGetSession.d.ts +6 -0
- package/dist/hooks/session/useGetSession.d.ts.map +1 -1
- package/dist/hooks/session/useGetSession.js +17 -6
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +15 -17
- 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,+CAuVzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -31,13 +31,12 @@ const DeskConversationList = () => {
|
|
|
31
31
|
const searchParams = useSearchParams();
|
|
32
32
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
33
33
|
const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
|
|
34
|
-
const conversationList = useConversationStore((state) => state.conversationList);
|
|
35
34
|
const updateConversationList = useConversationStore((state) => state.updateConversationList);
|
|
36
35
|
const filterSummary = useSessionStore((state) => state.filterSummary);
|
|
37
36
|
const viewMode = useSessionStore((state) => state.viewMode);
|
|
38
37
|
const isAllGroupsView = viewMode === "allGroups";
|
|
39
38
|
const { data: teamSummaries } = useGetTeamSessionSummary();
|
|
40
|
-
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, } = useGetSession({
|
|
39
|
+
const { dataFlatten: rawSessions, hasNextPage, fetchNextPage, refetch, isSyncing, } = useGetSession({
|
|
41
40
|
filter: filterSummary,
|
|
42
41
|
options: { isEnabled: !isAllGroupsView },
|
|
43
42
|
});
|
|
@@ -89,7 +88,13 @@ const DeskConversationList = () => {
|
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
90
|
setSelectedConversationId(threadId);
|
|
92
|
-
|
|
91
|
+
// Read the snapshot instead of subscribing — this effect must only
|
|
92
|
+
// re-run on threadId change. Depending on conversationList re-fired it
|
|
93
|
+
// on every unrelated mutation (including its own updateConversationList
|
|
94
|
+
// call below), looping the sidebar between loaded and loading.
|
|
95
|
+
const selectedConversation = useConversationStore
|
|
96
|
+
.getState()
|
|
97
|
+
.conversationList.find((conv) => conv.conversationID === threadId);
|
|
93
98
|
// threadId can come from a notification/stale search click — force a
|
|
94
99
|
// fresh membership check instead of trusting the groupID cache.
|
|
95
100
|
if (selectedConversation) {
|
|
@@ -110,7 +115,7 @@ const DeskConversationList = () => {
|
|
|
110
115
|
}
|
|
111
116
|
});
|
|
112
117
|
}
|
|
113
|
-
}, [searchParams
|
|
118
|
+
}, [searchParams]);
|
|
114
119
|
useEffect(() => {
|
|
115
120
|
const handler = (sessionUpdated) => {
|
|
116
121
|
if (sessionUpdated.status !== filterSummary.status ||
|
|
@@ -131,7 +136,12 @@ const DeskConversationList = () => {
|
|
|
131
136
|
}, className: "rounded-lg text-sm flex-1 h-[36px]", size: "large", allowClear: true, onClick: setShowSearchTrue, value: search, autoFocus: false, onClear: onCloseSearch }), showSearch ? (_jsx(Button, { onClick: onCloseSearch, variant: "outlined", className: "p-0 w-[36px] h-[36px] text-gray-500", children: _jsx(Icon, { icon: "close-b", size: 22 }) })) : (_jsx(Button, { "data-testid": "conversation-list-compose-icon", onClick: () => setIsComposeOpen(true), variant: "outlined", className: "p-0 w-[36px] h-[36px] shrink-0 text-gray-500", children: _jsx(Icon, { icon: "edit-b", size: 20 }) }))] }), _jsxs("div", { className: "flex items-center justify-between mt-2", children: [_jsx("span", { className: "text-sm font-semibold text-blue-500 truncate", children: pathName }), isAllGroupsView ? (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: "user-three-o", size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t("all_groups") })] })) : (filterConfig && (_jsxs("span", { className: "flex items-center gap-1.5 shrink-0", children: [_jsx(Icon, { icon: filterConfig.iconName, size: 16, className: "text-gray-500" }), _jsx("span", { className: "text-sm font-semibold text-gray-800", children: t(filterConfig.labelKey) })] })))] })] }), _jsxs("div", { className: "relative flex-1 min-h-0", children: [_jsx("div", { id: "scrollableConversationsDiv", style: {
|
|
132
137
|
height: "100%",
|
|
133
138
|
overflow: showSearch ? "hidden" : "auto",
|
|
134
|
-
}, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }))) : (_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children:
|
|
139
|
+
}, children: isAllGroupsView ? (isConversationGroupsLoading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : !(conversationGroups === null || conversationGroups === void 0 ? void 0 : conversationGroups.length) ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : (_jsx(InfiniteScroll, { dataLength: conversationGroups.length, next: fetchNextGroupsPage, hasMore: !!hasNextGroupsPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: conversationGroups.map((group) => (_jsx(ConversationByGroupItem, { group: group, latestPreview: groupLatestPreviewMap.get(group.conversationId) }, group.groupId))) }))) : (_jsx(InfiniteScroll, { dataLength: (sessions === null || sessions === void 0 ? void 0 : sessions.length) || 0, next: fetchNextPage, hasMore: hasNextPage, loader: _jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) }), scrollableTarget: "scrollableConversationsDiv", children: isSyncing ? (
|
|
140
|
+
// conversationList (the client cache dataFlatten joins
|
|
141
|
+
// against) hasn't caught up with this filter's BE response
|
|
142
|
+
// yet — showing the list now would flash a wrong, too-low
|
|
143
|
+
// count that corrects itself a moment later.
|
|
144
|
+
_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Spin, {}) })) : (sessions === null || sessions === void 0 ? void 0 : sessions.length) === 0 ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Empty, { image: _jsx(Icon, { icon: "chat-square-b", size: 80, className: "text-gray-300" }), description: t("no_conversation") }) })) : ((_b = sessions === null || sessions === void 0 ? void 0 : sessions.map) === null || _b === void 0 ? void 0 : _b.call(sessions, (session) => (_jsx(ConversationBySessionItem, { sessionItem: session, latestPreview: latestPreviewMap.get(session.conversationId) }, session.conversationId)))) })) }), _jsx(Drawer, { open: showSearch, mask: false, closeIcon: false, styles: {
|
|
135
145
|
body: {
|
|
136
146
|
padding: 0,
|
|
137
147
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAiCA,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,4CAkV3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -24,6 +24,7 @@ import { useJoinGroupFlow } from "../../hooks/session/useJoinGroupFlow";
|
|
|
24
24
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
25
25
|
import { isGroupConversation } from "../../utils/imCommon";
|
|
26
26
|
import { ConnectStatus, SessionStatus } from "../../types/chat";
|
|
27
|
+
import { PeerType } from "../../types/conversation";
|
|
27
28
|
import PinLimitModal from "../../components/conversation-detail/PinLimitModal";
|
|
28
29
|
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
29
30
|
dayjs.extend(isToday);
|
|
@@ -34,29 +35,36 @@ const MessageList = (props) => {
|
|
|
34
35
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
35
36
|
const permissions = useGroupPermission(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID);
|
|
36
37
|
const isGroupChat = isGroupConversation(conversationData !== null && conversationData !== void 0 ? conversationData : undefined);
|
|
38
|
+
// A CX/support session can ride a Group session with peerType missing on
|
|
39
|
+
// the instant response (see isGroupConversation) — non-member/mute checks
|
|
40
|
+
// must not fire off that loose signal, or a support thread with no group
|
|
41
|
+
// membership record gets wrongly treated as a blocked group member.
|
|
42
|
+
const isConfirmedGroup = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.GROUP;
|
|
37
43
|
const groupID = conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID;
|
|
38
44
|
const currentGroupInfo = useConversationStore((state) => groupID ? state.currentGroupInfo.get(groupID) : undefined);
|
|
39
45
|
const currentMemberInGroup = useConversationStore((state) => groupID ? state.currentMemberInGroup.get(groupID) : undefined);
|
|
40
46
|
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
47
|
+
const setIsViewOnlyMember = useConversationStore((state) => state.setIsViewOnlyMember);
|
|
41
48
|
// "Fresh" now means "we have a cache entry for this groupID" — populated
|
|
42
49
|
// either by a fetch just now or a cache hit from earlier in the session,
|
|
43
50
|
// not necessarily a response that JUST arrived (that's the point of the
|
|
44
51
|
// groupID cache: no spinner on reopening a previously-viewed group).
|
|
45
52
|
const isGroupInfoFresh = currentGroupInfo !== undefined;
|
|
46
|
-
const isGroupStateFresh = !
|
|
53
|
+
const isGroupStateFresh = !isConfirmedGroup ||
|
|
47
54
|
(isGroupInfoFresh &&
|
|
48
55
|
(!currentMemberInGroup || currentMemberInGroup.roleLevel !== undefined));
|
|
49
56
|
// Group-wide mute (GroupThreadInfo.tsx) restricts Normal members only.
|
|
50
|
-
const isRestrictedByGroupMute =
|
|
57
|
+
const isRestrictedByGroupMute = isConfirmedGroup &&
|
|
51
58
|
(currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status) === GroupStatus.Muted &&
|
|
52
59
|
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Normal;
|
|
53
|
-
// useGroupPermission defaults to Normal-role
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
const
|
|
60
|
+
// useGroupPermission defaults to Normal-role when currentMemberInGroup
|
|
61
|
+
// is undefined — wrong once absent for good, not just not-loaded-yet.
|
|
62
|
+
// Gated on isConfirmedGroup: a support thread has no membership record
|
|
63
|
+
// by design, not because it's a removed member.
|
|
64
|
+
const isConfirmedNonMember = isConfirmedGroup && isGroupInfoFresh && !currentMemberInGroup;
|
|
65
|
+
const canSendMessage = !isConfirmedGroup ||
|
|
58
66
|
(!isConfirmedNonMember && permissions.sendMessage && !isRestrictedByGroupMute);
|
|
59
|
-
const canPinMessage = !
|
|
67
|
+
const canPinMessage = !isConfirmedGroup || (!isConfirmedNonMember && permissions.pinMessage);
|
|
60
68
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
61
69
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
62
70
|
const [openMenuId, setOpenMenuId] = useState(null);
|
|
@@ -71,16 +79,24 @@ const MessageList = (props) => {
|
|
|
71
79
|
const pinnedTotalCount = useConversationStore((state) => state.pinnedTotalCount);
|
|
72
80
|
const pinnedRemainingCount = useConversationStore((state) => state.pinnedRemainingCount);
|
|
73
81
|
const [pinLimitModalOpen, setPinLimitModalOpen] = useState(false);
|
|
74
|
-
const {
|
|
82
|
+
const { rawGroupId, latestConversationSession, refetchConversationSessions, } = useConversationSessionState({
|
|
75
83
|
conversationId,
|
|
76
84
|
conversationType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType,
|
|
77
85
|
peerType: conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType,
|
|
78
86
|
groupId: conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID,
|
|
79
87
|
});
|
|
80
88
|
const { isJoined, isCheckingMembership, shouldRestrictToLastSession, joinGroup, isJoining, } = useJoinGroupFlow({
|
|
81
|
-
groupId,
|
|
89
|
+
groupId: rawGroupId,
|
|
82
90
|
sessionId: latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.id,
|
|
83
91
|
});
|
|
92
|
+
// A stale isViewOnlyMember=true only ever clears via the WASM join-group
|
|
93
|
+
// path or a WS event — neither fires for a support thread joined via
|
|
94
|
+
// useJoinSession's REST call. Sync it off once isJoined confirms true.
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!isConfirmedGroup && isJoined && isViewOnlyMember) {
|
|
97
|
+
setIsViewOnlyMember(false);
|
|
98
|
+
}
|
|
99
|
+
}, [isConfirmedGroup, isJoined, isViewOnlyMember, setIsViewOnlyMember]);
|
|
84
100
|
const messageController = useMessage(conversationId, searchClientMsgID);
|
|
85
101
|
const { loadState, moreOldLoading, moreNewLoading } = messageController;
|
|
86
102
|
const { scrollRef, loadMoreOldMessage, handleInfiniteScroll, scrollToBottom, scrollToMessage, } = useMessageListScroll(Object.assign({ conversationId, userId: user === null || user === void 0 ? void 0 : user.userID, isGroupChat }, messageController));
|
|
@@ -164,7 +180,7 @@ const MessageList = (props) => {
|
|
|
164
180
|
if (!conversationData) {
|
|
165
181
|
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") })) }));
|
|
166
182
|
}
|
|
167
|
-
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "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, {}) })), (
|
|
183
|
+
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 overflow-hidden bg-white", "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, {}) })), (Boolean(rawGroupId) && 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 })) : !isConfirmedGroup && shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", "data-cp-name": "JoinGroupRequired", 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 ? (
|
|
168
184
|
// Brief fallback while isJoined catches up after the kick event.
|
|
169
185
|
_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) })] }));
|
|
170
186
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AtTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/AtTextMessage.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAM3D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"AtTextMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/AtTextMessage.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAM3D,OAAO,KAAK,EAEV,aAAa,EAEd,MAAM,cAAc,CAAC;AAGtB,UAAU,sBAAsB;IAC9B,OAAO,EAAE,WAAW,CAAC;CACtB;AAOD,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,EACZ,eAAe,MAAM,EAAE,KACtB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAsC5C,CAAC;AAKF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,SAAS,aAAa,EAAE,KACvB,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAsB5C,CAAC;AAeF,QAAA,MAAM,iBAAiB,GAAI,OAAO,sBAAsB,mDA6EvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Fragment } from "react";
|
|
4
4
|
import clsx from "clsx";
|
|
5
|
-
import { extractLinkUrls } from "../../../utils/link";
|
|
6
5
|
import LinkText from "./LinkText";
|
|
7
6
|
import { MESSAGE_TEXT_CLASS } from "./textStyle";
|
|
8
7
|
import { useTranslation } from "react-i18next";
|
|
9
8
|
import { AT_ALL_TAG } from "../../../hooks/message/useSendMessage";
|
|
9
|
+
import LinkCard from "./LinkCard";
|
|
10
10
|
// AtTextElem has no offset/index, so mentions are recovered by matching
|
|
11
11
|
// "@displayName" text: longest-label-first (avoids "@Anna" being cut short
|
|
12
12
|
// by "@An"), each label consumed only as many times as it's really mentioned.
|
|
@@ -34,7 +34,10 @@ export const splitByMentions = (text, mentionLabels) => {
|
|
|
34
34
|
break;
|
|
35
35
|
}
|
|
36
36
|
if (match.index > cursor) {
|
|
37
|
-
segments.push({
|
|
37
|
+
segments.push({
|
|
38
|
+
text: text.slice(cursor, match.index),
|
|
39
|
+
isMention: false,
|
|
40
|
+
});
|
|
38
41
|
}
|
|
39
42
|
segments.push({ text: match.label, isMention: true });
|
|
40
43
|
remainingCount.set(match.label, ((_b = remainingCount.get(match.label)) !== null && _b !== void 0 ? _b : 0) - 1);
|
|
@@ -55,7 +58,10 @@ export const splitByOffsets = (text, offsets) => {
|
|
|
55
58
|
if (offset > cursor) {
|
|
56
59
|
segments.push({ text: text.slice(cursor, offset), isMention: false });
|
|
57
60
|
}
|
|
58
|
-
segments.push({
|
|
61
|
+
segments.push({
|
|
62
|
+
text: text.slice(offset, offset + length),
|
|
63
|
+
isMention: true,
|
|
64
|
+
});
|
|
59
65
|
cursor = offset + length;
|
|
60
66
|
}
|
|
61
67
|
if (cursor < text.length) {
|
|
@@ -77,20 +83,26 @@ const parseMentionOffsets = (ex) => {
|
|
|
77
83
|
}
|
|
78
84
|
};
|
|
79
85
|
const AtTextMessageItem = (props) => {
|
|
80
|
-
var _a, _b, _c;
|
|
86
|
+
var _a, _b, _c, _d, _e;
|
|
81
87
|
const { message } = props;
|
|
82
88
|
const { t } = useTranslation();
|
|
83
89
|
const atTextElem = message === null || message === void 0 ? void 0 : message.atTextElem;
|
|
84
90
|
if (!(atTextElem === null || atTextElem === void 0 ? void 0 : atTextElem.text))
|
|
85
91
|
return null;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
let extendMessageInfo = null;
|
|
93
|
+
try {
|
|
94
|
+
extendMessageInfo = JSON.parse((message === null || message === void 0 ? void 0 : message.ex) || "{}");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
extendMessageInfo = {};
|
|
98
|
+
console.error("Failed to parse extendMessageInfo", error);
|
|
93
99
|
}
|
|
100
|
+
const urlMetadata = extendMessageInfo === null || extendMessageInfo === void 0 ? void 0 : extendMessageInfo.urlMetadata;
|
|
101
|
+
const metadataUrl = (_b = (_a = message === null || message === void 0 ? void 0 : message.urlTextElem) === null || _a === void 0 ? void 0 : _a.urls) === null || _b === void 0 ? void 0 : _b[0];
|
|
102
|
+
const hasUrlMetadata = !!(urlMetadata &&
|
|
103
|
+
(urlMetadata.title || urlMetadata.description || urlMetadata.image));
|
|
104
|
+
const isAtAll = (_c = atTextElem.atUserList) === null || _c === void 0 ? void 0 : _c.some((userID) => userID === AT_ALL_TAG);
|
|
105
|
+
const memberMentionLabels = (_e = (_d = atTextElem.atUsersInfo) === null || _d === void 0 ? void 0 : _d.map((info) => `@${info.groupNickname}`)) !== null && _e !== void 0 ? _e : [];
|
|
94
106
|
const mentionOffsets = parseMentionOffsets(message === null || message === void 0 ? void 0 : message.ex);
|
|
95
107
|
let segments;
|
|
96
108
|
if (mentionOffsets) {
|
|
@@ -107,6 +119,6 @@ const AtTextMessageItem = (props) => {
|
|
|
107
119
|
];
|
|
108
120
|
segments = splitByMentions(atTextElem.text, mentionLabels);
|
|
109
121
|
}
|
|
110
|
-
return (_jsx("p", { className: clsx("min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere]", MESSAGE_TEXT_CLASS), children: segments.map((segment, index) => (_jsx(Fragment, { children: segment.isMention ? (_jsx("span", { className: "font-medium text-[var(--dc-color-primary)]", children: segment.text })) : (segment.text) }, index))) }));
|
|
122
|
+
return (_jsxs("div", { className: clsx("flex flex-col gap-2", hasUrlMetadata ? "max-w-[292px]" : ""), children: [_jsx("p", { className: clsx("min-w-0 whitespace-pre-wrap [overflow-wrap:anywhere]", MESSAGE_TEXT_CLASS), children: segments.map((segment, index) => (_jsx(Fragment, { children: segment.isMention ? (_jsx("span", { className: "font-medium text-[var(--dc-color-primary)]", children: segment.text })) : (_jsx(LinkText, { text: segment.text })) }, index))) }), hasUrlMetadata && urlMetadata && (_jsx(LinkCard, { metadata: urlMetadata, url: metadataUrl }))] }));
|
|
111
123
|
};
|
|
112
124
|
export default AtTextMessageItem;
|
|
@@ -5,7 +5,7 @@ import useAuthStore from "../../../store/auth";
|
|
|
5
5
|
import useConversationStore from "../../../store/conversation";
|
|
6
6
|
import ReplyQuoteContent from "../../../components/message/reply/ReplyQuoteContent";
|
|
7
7
|
const QuoteMessageItem = ({ message, onPressQuoteMessage, }) => {
|
|
8
|
-
var _a;
|
|
8
|
+
var _a, _b;
|
|
9
9
|
const userID = useAuthStore((state) => state.userID);
|
|
10
10
|
const isCrm = useAuthStore((state) => state.isCrm);
|
|
11
11
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
@@ -29,7 +29,7 @@ const QuoteMessageItem = ({ message, onPressQuoteMessage, }) => {
|
|
|
29
29
|
return t("you");
|
|
30
30
|
return name;
|
|
31
31
|
}, [isCrm, isChatSupport, conversationData === null || conversationData === void 0 ? void 0 : conversationData.showName, userID, t]);
|
|
32
|
-
const quoteMessage = (_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage;
|
|
32
|
+
const quoteMessage = ((_a = message === null || message === void 0 ? void 0 : message.quoteElem) === null || _a === void 0 ? void 0 : _a.quoteMessage) || ((_b = message === null || message === void 0 ? void 0 : message.atTextElem) === null || _b === void 0 ? void 0 : _b.quoteMessage);
|
|
33
33
|
if (!quoteMessage)
|
|
34
34
|
return null;
|
|
35
35
|
const title = getDisplayName({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderMessageContent.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/renderMessageContent.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"renderMessageContent.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/renderMessageContent.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,IAAI,eAAe,EAE/B,MAAM,8BAA8B,CAAC;AAetC,eAAO,MAAM,mBAAmB,GAAI,SAAS,eAAe,4CA6C3D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,eAAe,EACxB,sBAAsB,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,4CAwBpD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { MessageType } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
import { MessageType, } from "@droppii-org/wasm-client-sdk";
|
|
3
3
|
import TextMessageItem from "./TextMessage";
|
|
4
4
|
import ImageMessageItem from "./ImageMessage";
|
|
5
5
|
import FileMessageItem from "./FileMessage";
|
|
@@ -51,16 +51,16 @@ export const renderMessageByType = (message) => {
|
|
|
51
51
|
case MessageType.GroupMuted:
|
|
52
52
|
case MessageType.GroupCancelMuted:
|
|
53
53
|
case MessageType.GroupAnnouncementUpdated:
|
|
54
|
-
case MessageType.GroupNameUpdated:
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
}
|
|
54
|
+
case MessageType.GroupNameUpdated: {
|
|
55
|
+
return _jsx(GroupNotificationMessageItem, { message: message });
|
|
56
|
+
}
|
|
58
57
|
default:
|
|
59
58
|
return _jsx(TextMessageItem, { message: message });
|
|
60
59
|
}
|
|
61
60
|
};
|
|
62
61
|
export const renderBubbleContent = (message, onPressQuoteMessage) => {
|
|
63
|
-
|
|
62
|
+
var _a;
|
|
63
|
+
if ((message === null || message === void 0 ? void 0 : message.quoteElem) || ((_a = message === null || message === void 0 ? void 0 : message.atTextElem) === null || _a === void 0 ? void 0 : _a.quoteMessage)) {
|
|
64
64
|
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(QuoteMessageItem, { message: message, onPressQuoteMessage: onPressQuoteMessage }), renderMessageByType(message)] }));
|
|
65
65
|
}
|
|
66
66
|
if ((message === null || message === void 0 ? void 0 : message.contentType) === MessageType.MergeMessage) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,cAAc,YA8iB1B,CAAC"}
|
|
@@ -14,7 +14,7 @@ import { useRefetchChatToken } from "../../hooks/init/useChatToken";
|
|
|
14
14
|
import { updateSession } from "../../hooks/session/useUpdateSession";
|
|
15
15
|
import { QUERY_KEYS } from "../../services/query";
|
|
16
16
|
import { useQueryClient } from "@tanstack/react-query";
|
|
17
|
-
import {
|
|
17
|
+
import { PeerType } from "../../types/conversation";
|
|
18
18
|
import { ignoreInternalLatestMessage } from "../../utils/common";
|
|
19
19
|
import { invalidateSessionQueries } from "../../utils/queryHelpers";
|
|
20
20
|
import emitter from "../../utils/events";
|
|
@@ -39,7 +39,12 @@ export const useGlobalEvent = () => {
|
|
|
39
39
|
});
|
|
40
40
|
const refreshCurrentGroupState = async () => {
|
|
41
41
|
const currentConversation = useConversationStore.getState().currentConversation;
|
|
42
|
-
|
|
42
|
+
// Strict peerType check, matching updateCurrentConversation — also
|
|
43
|
+
// the retry path for a real group whose peerType was unconfirmed at
|
|
44
|
+
// open time, since this reruns on every OnConversationChanged/
|
|
45
|
+
// OnNewConversation.
|
|
46
|
+
if (!(currentConversation === null || currentConversation === void 0 ? void 0 : currentConversation.groupID) ||
|
|
47
|
+
currentConversation.peerType !== PeerType.GROUP) {
|
|
43
48
|
return;
|
|
44
49
|
}
|
|
45
50
|
// allSettled, not all — getSelfGroupPermissionByReq's underlying wasm
|
|
@@ -17,7 +17,7 @@ export declare const createAtTextMessage: (params: AtMsgParams) => Promise<Messa
|
|
|
17
17
|
export declare const createUrlTextMessage: (text: string, urls: string[]) => Promise<MessageItem | null>;
|
|
18
18
|
export declare const createCustomMessageByParams: (params: CustomMsgParams) => Promise<MessageItem | null>;
|
|
19
19
|
export declare const createCustomMessage: (payload: CustomMessagePayload) => Promise<MessageItem | null>;
|
|
20
|
-
export declare const createQuoteMessage: (text: string, quotedMessage: MessageItem) => Promise<MessageItem | null>;
|
|
20
|
+
export declare const createQuoteMessage: (text: string, quotedMessage: MessageItem, urls?: string[]) => Promise<MessageItem | null>;
|
|
21
21
|
export type SendMessageScrollEvent = "CHAT_LIST_SCROLL_TO_BOTTOM" | "CONVERSATION_CHAT_SCROLL_TO_BOTTOM_IF_AT_BOTTOM";
|
|
22
22
|
export interface UseSendMessageOptions {
|
|
23
23
|
scrollEvent?: SendMessageScrollEvent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,EACf,eAAe,EACf,WAAW,EAIZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAWtB,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAQlC,OAAO,
|
|
1
|
+
{"version":3,"file":"useSendMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useSendMessage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,eAAe,EACf,eAAe,EACf,WAAW,EAIZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAWtB,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAQlC,OAAO,EAEL,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AAmBrB,eAAO,MAAM,iBAAiB,GAAI,SAAS;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,YAE0C,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAAU,MAAM,MAAM,gCAanD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,iBAAiB,eAAe,gCAazE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,MAAM,oBAAoB,gCAaxE,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,gCAatE,CAAC;AAGF,eAAO,MAAM,UAAU,aAAa,CAAC;AAErC,eAAO,MAAM,mBAAmB,GAAU,QAAQ,WAAW,gCAa5D,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAU,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,gCActE,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAU,QAAQ,eAAe,gCAaxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAU,SAAS,oBAAoB,gCAOtE,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,eAAe,WAAW,EAC1B,OAAO,MAAM,EAAE,gCAehB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B,4BAA4B,GAC5B,iDAAiD,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,sBAAsB,CAAC;CACtC;AAED,eAAO,MAAM,cAAc,GAAI,UAAU,qBAAqB;gIAwSvD;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;QACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC;QAClC,UAAU,EAAE,OAAO,CAAC;QACpB,WAAW,CAAC,EAAE,eAAe,CAAC;QAC9B,IAAI,CAAC,EAAE,WAAW,CAAC;QACnB,aAAa,CAAC,EAAE,oBAAoB,CAAC;QACrC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;KAC/B,KAAG,OAAO,CAAC,OAAO,CAAC;iCA7CT,oBAAoB,gBACf;QAAE,cAAc,CAAC,EAAE,gBAAgB,CAAA;KAAE,KAClD,OAAO,CAAC,OAAO,CAAC;mCAnHG,WAAW;CAsXpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,kEAMvC;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC,sBAkCA,CAAC"}
|
|
@@ -122,10 +122,11 @@ export const createCustomMessage = async (payload) => {
|
|
|
122
122
|
description: payload.type === CustomMessageType.Order ? payload.data.orderCode : "",
|
|
123
123
|
});
|
|
124
124
|
};
|
|
125
|
-
export const createQuoteMessage = async (text, quotedMessage) => {
|
|
125
|
+
export const createQuoteMessage = async (text, quotedMessage, urls) => {
|
|
126
126
|
const textMessage = await DChatSDK.createQuoteMessage({
|
|
127
127
|
text,
|
|
128
128
|
message: JSON.stringify(quotedMessage),
|
|
129
|
+
urls,
|
|
129
130
|
})
|
|
130
131
|
.then(({ data }) => {
|
|
131
132
|
return data;
|
|
@@ -202,7 +203,9 @@ export const useSendMessage = (options) => {
|
|
|
202
203
|
title: pushMeta.title,
|
|
203
204
|
userId: userId,
|
|
204
205
|
groupId: groupID,
|
|
205
|
-
botId: conversationData
|
|
206
|
+
botId: conversationData
|
|
207
|
+
? resolveConversationBotId(conversationData)
|
|
208
|
+
: "",
|
|
206
209
|
desc,
|
|
207
210
|
}),
|
|
208
211
|
iOSPushSound: "default",
|
|
@@ -345,7 +348,10 @@ export const useSendMessage = (options) => {
|
|
|
345
348
|
const messageList = [];
|
|
346
349
|
const imageFileWrappers = files.filter((f) => { var _a; return (_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("image/"); });
|
|
347
350
|
const videoFileWrappers = files.filter((f) => { var _a; return (_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("video/"); });
|
|
348
|
-
const documentFileWrappers = files.filter((f) => {
|
|
351
|
+
const documentFileWrappers = files.filter((f) => {
|
|
352
|
+
var _a;
|
|
353
|
+
return (_a = f.originFileObj) === null || _a === void 0 ? void 0 : _a.type.startsWith("application/");
|
|
354
|
+
});
|
|
349
355
|
const imageMessages = [];
|
|
350
356
|
for (const fileWrapper of imageFileWrappers) {
|
|
351
357
|
const file = fileWrapper.originFileObj;
|
|
@@ -476,11 +482,12 @@ export const useSendMessage = (options) => {
|
|
|
476
482
|
: mentions.atUserIDList,
|
|
477
483
|
atUsersInfo: mentions.atUsersInfo,
|
|
478
484
|
message: (quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID) ? quotedMessage : undefined,
|
|
485
|
+
urls: urls || undefined,
|
|
479
486
|
});
|
|
480
487
|
setQuotedMessage(null);
|
|
481
488
|
}
|
|
482
489
|
else if (quotedMessage === null || quotedMessage === void 0 ? void 0 : quotedMessage.clientMsgID) {
|
|
483
|
-
textMessage = await createQuoteMessage(plainText, quotedMessage);
|
|
490
|
+
textMessage = await createQuoteMessage(plainText, quotedMessage, urls || undefined);
|
|
484
491
|
setQuotedMessage(null);
|
|
485
492
|
}
|
|
486
493
|
else if (urls.length > 0) {
|
|
@@ -506,9 +513,7 @@ export const useSendMessage = (options) => {
|
|
|
506
513
|
(message === null || message === void 0 ? void 0 : message.contentType) === MessageType.AtTextMessage;
|
|
507
514
|
const extendMessageInfo = generateExtendMessageInfo({
|
|
508
515
|
richText: isTextLikeMessage ? richText : "",
|
|
509
|
-
urlMetadata:
|
|
510
|
-
? urlMetadata
|
|
511
|
-
: undefined,
|
|
516
|
+
urlMetadata: urlMetadata || undefined,
|
|
512
517
|
currentSession,
|
|
513
518
|
flow: isTextLikeMessage ? flow : undefined,
|
|
514
519
|
mentionOffsets: (message === null || message === void 0 ? void 0 : message.contentType) === MessageType.AtTextMessage
|
|
@@ -5,8 +5,9 @@ interface UseConversationSessionStateParams {
|
|
|
5
5
|
peerType?: string;
|
|
6
6
|
groupId?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare const useConversationSessionState: ({ conversationId,
|
|
8
|
+
export declare const useConversationSessionState: ({ conversationId, peerType, groupId, }: UseConversationSessionStateParams) => {
|
|
9
9
|
groupId: string | undefined;
|
|
10
|
+
rawGroupId: string | undefined;
|
|
10
11
|
latestConversationSession: import("../../types/dto").ISessionResponse | undefined;
|
|
11
12
|
customerUserID: string;
|
|
12
13
|
refetchConversationSessions: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<import("../../types/dto").ISessionResponse[], Error>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationSessionState.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useConversationSessionState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,iCAAiC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GAAI,
|
|
1
|
+
{"version":3,"file":"useConversationSessionState.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useConversationSessionState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAI3D,UAAU,iCAAiC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,2BAA2B,GAAI,wCAIzC,iCAAiC;;;;;;CA6BnC,CAAC"}
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useGetListSessionByConversation } from "./useGetListSessionByConversation";
|
|
3
|
-
import {
|
|
4
|
-
export const useConversationSessionState = ({ conversationId,
|
|
3
|
+
import { PeerType } from "../../types/conversation";
|
|
4
|
+
export const useConversationSessionState = ({ conversationId, peerType, groupId, }) => {
|
|
5
5
|
var _a;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
// Strict check, not isGroupConversation's loose conversationType fallback
|
|
7
|
+
// — that wrongly disabled the session-list fetch and ran group-permission
|
|
8
|
+
// checks against a support/bot thread's non-member-managed groupID.
|
|
9
|
+
const isConfirmedGroup = peerType === PeerType.GROUP;
|
|
10
|
+
const currentGroupId = isConfirmedGroup ? groupId : undefined;
|
|
8
11
|
const { data: conversationSessions, refetch: refetchConversationSessions, } = useGetListSessionByConversation({
|
|
9
12
|
conversationId,
|
|
10
|
-
enabled: !
|
|
13
|
+
enabled: !isConfirmedGroup,
|
|
11
14
|
});
|
|
12
15
|
const latestConversationSession = conversationSessions === null || conversationSessions === void 0 ? void 0 : conversationSessions[0];
|
|
13
16
|
return {
|
|
17
|
+
// Gated: undefined unless it's a confirmed real group — for
|
|
18
|
+
// permission-side APIs (getSelfGroupPermissionByReq etc.) that must
|
|
19
|
+
// never run against a support/bot thread's underlying groupID.
|
|
14
20
|
groupId: currentGroupId,
|
|
21
|
+
// Raw, ungated: useJoinGroupFlow's isJoinGroup check needs the real
|
|
22
|
+
// groupID even for a support/bot thread, or it never resolves.
|
|
23
|
+
rawGroupId: groupId,
|
|
15
24
|
latestConversationSession,
|
|
16
25
|
customerUserID: (_a = latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.ownerId) !== null && _a !== void 0 ? _a : "",
|
|
17
26
|
refetchConversationSessions,
|
|
@@ -42,6 +42,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
42
42
|
hasNextPage: boolean;
|
|
43
43
|
isFetchingNextPage: boolean;
|
|
44
44
|
isLoading: boolean;
|
|
45
|
+
isSyncing: boolean;
|
|
45
46
|
dataFlatten: ISessionByStatus[];
|
|
46
47
|
} | {
|
|
47
48
|
error: null;
|
|
@@ -78,6 +79,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
78
79
|
hasNextPage: boolean;
|
|
79
80
|
isFetchingNextPage: boolean;
|
|
80
81
|
isLoading: boolean;
|
|
82
|
+
isSyncing: boolean;
|
|
81
83
|
dataFlatten: ISessionByStatus[];
|
|
82
84
|
} | {
|
|
83
85
|
error: Error;
|
|
@@ -114,6 +116,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
114
116
|
hasNextPage: boolean;
|
|
115
117
|
isFetchingNextPage: boolean;
|
|
116
118
|
isLoading: boolean;
|
|
119
|
+
isSyncing: boolean;
|
|
117
120
|
dataFlatten: ISessionByStatus[];
|
|
118
121
|
} | {
|
|
119
122
|
error: null;
|
|
@@ -150,6 +153,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
150
153
|
hasNextPage: boolean;
|
|
151
154
|
isFetchingNextPage: boolean;
|
|
152
155
|
isLoading: boolean;
|
|
156
|
+
isSyncing: boolean;
|
|
153
157
|
dataFlatten: ISessionByStatus[];
|
|
154
158
|
} | {
|
|
155
159
|
error: null;
|
|
@@ -186,6 +190,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
186
190
|
hasNextPage: boolean;
|
|
187
191
|
isFetchingNextPage: boolean;
|
|
188
192
|
isLoading: boolean;
|
|
193
|
+
isSyncing: boolean;
|
|
189
194
|
dataFlatten: ISessionByStatus[];
|
|
190
195
|
} | {
|
|
191
196
|
isError: false;
|
|
@@ -222,6 +227,7 @@ export declare const useGetSession: ({ filter, options, }: UseGetSessionProps) =
|
|
|
222
227
|
hasNextPage: boolean;
|
|
223
228
|
isFetchingNextPage: boolean;
|
|
224
229
|
isLoading: boolean;
|
|
230
|
+
isSyncing: boolean;
|
|
225
231
|
dataFlatten: ISessionByStatus[];
|
|
226
232
|
};
|
|
227
233
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGetSession.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useGetSession.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMhE,UAAU,kBAAkB;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AA6JD,eAAO,MAAM,aAAa,GAAI,sBAG3B,kBAAkB
|
|
1
|
+
{"version":3,"file":"useGetSession.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useGetSession.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,YAAY,EAIb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMhE,UAAU,kBAAkB;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AA6JD,eAAO,MAAM,aAAa,GAAI,sBAG3B,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiFpB,CAAC"}
|
|
@@ -160,23 +160,34 @@ export const useGetSession = ({ filter, options = { pageSize: undefined, isEnabl
|
|
|
160
160
|
},
|
|
161
161
|
enabled: isEnabled && hasValidFilter(filter),
|
|
162
162
|
}), { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = _c, rest = __rest(_c, ["data", "fetchNextPage", "hasNextPage", "isFetchingNextPage", "isLoading"]);
|
|
163
|
+
// conversationList is filled in asynchronously by syncSessionConversationsToStore
|
|
164
|
+
// below — joining against it mid-sync would under-count the list. isSyncing
|
|
165
|
+
// tells the consumer to keep showing its loading state until it catches up.
|
|
163
166
|
const dataFlatten = useMemo(() => {
|
|
164
|
-
var _a;
|
|
165
167
|
if (!data)
|
|
166
168
|
return [];
|
|
167
169
|
const allItems = data.pages.flatMap((page) => page.data);
|
|
168
|
-
const
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
const conversationMap = new Map(conversationList === null || conversationList === void 0 ? void 0 : conversationList.map((c) => [c.conversationID, c]));
|
|
171
|
+
return allItems
|
|
172
|
+
.map((session) => {
|
|
173
|
+
const conversation = conversationMap.get(session.conversationId);
|
|
174
|
+
if (!conversation)
|
|
172
175
|
return null;
|
|
173
176
|
return Object.assign(Object.assign({}, session), { conversation });
|
|
174
|
-
})
|
|
177
|
+
})
|
|
178
|
+
.filter((item) => Boolean(item));
|
|
175
179
|
}, [data, conversationList]);
|
|
180
|
+
const isSyncing = useMemo(() => {
|
|
181
|
+
if (!data)
|
|
182
|
+
return false;
|
|
183
|
+
const allItems = data.pages.flatMap((page) => page.data);
|
|
184
|
+
return allItems.length !== dataFlatten.length;
|
|
185
|
+
}, [data, dataFlatten]);
|
|
176
186
|
return Object.assign({ data,
|
|
177
187
|
fetchNextPage,
|
|
178
188
|
hasNextPage,
|
|
179
189
|
isFetchingNextPage,
|
|
180
190
|
isLoading,
|
|
191
|
+
isSyncing,
|
|
181
192
|
dataFlatten }, rest);
|
|
182
193
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAEF,QAAA,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/store/conversation.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAY3C,eAAO,MAAM,kBAAkB,GAAI,SAAS,OAAO,SAElD,CAAC;AAEF,QAAA,MAAM,oBAAoB,gFAiYvB,CAAC;AAEJ,eAAe,oBAAoB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GroupVisibility, SessionType, } from "@droppii-org/wasm-client-sdk";
|
|
2
2
|
import { create } from "zustand";
|
|
3
3
|
import { DChatSDK } from "../constants/sdk";
|
|
4
|
-
import { conversationSort
|
|
4
|
+
import { conversationSort } from "../utils/imCommon";
|
|
5
5
|
import { PeerType } from "../types/conversation";
|
|
6
6
|
import useUserStore from "./user";
|
|
7
7
|
import useAuthStore from "./auth";
|
|
@@ -106,14 +106,15 @@ const useConversationStore = create((set, get) => ({
|
|
|
106
106
|
const prevConversation = get().currentConversation;
|
|
107
107
|
const toggleNewConversation = conversation.conversationID !== (prevConversation === null || prevConversation === void 0 ? void 0 : prevConversation.conversationID);
|
|
108
108
|
if (toggleNewConversation) {
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
const isConfirmedGroup = conversation.peerType === PeerType.GROUP;
|
|
110
|
+
const groupID = conversation.groupID;
|
|
111
|
+
set({ isViewOnlyMember: false });
|
|
112
|
+
if (isConfirmedGroup && groupID) {
|
|
111
113
|
// nonMemberGroupID only guards the PREVIOUS group's stale fetches
|
|
112
114
|
// — a re-opened group always gets a real fetch, even if same ID.
|
|
113
115
|
if (get().nonMemberGroupID !== groupID) {
|
|
114
116
|
set({ nonMemberGroupID: null });
|
|
115
117
|
}
|
|
116
|
-
set({ isViewOnlyMember: false });
|
|
117
118
|
// Cache hit: a previously-viewed group's info/membership is still
|
|
118
119
|
// in the Map — render from it immediately, no fetch, no spinner.
|
|
119
120
|
// forceRevalidate bypasses the cache hit for external entry points
|
|
@@ -140,19 +141,15 @@ const useConversationStore = create((set, get) => ({
|
|
|
140
141
|
get().getCurrentMemberInGroupByReq(groupID),
|
|
141
142
|
]);
|
|
142
143
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// its first await, which previously made this check unreliable.
|
|
150
|
-
// Require confirmed peerType — a CX/bot session can ride a Group
|
|
151
|
-
// session with peerType missing from a raw fetch; don't block on that.
|
|
152
|
-
const isConfirmedGroup = conversation.peerType === PeerType.GROUP;
|
|
144
|
+
}
|
|
145
|
+
// Derives view-only/block state from fetch/cache above for fresh
|
|
146
|
+
// loads and external entries. Unconfirmed (support/bot) threads get
|
|
147
|
+
// view-only-rejoin when not-a-member but never the block+modal path
|
|
148
|
+
// — that's reserved for isConfirmedGroup.
|
|
149
|
+
if (groupID) {
|
|
153
150
|
const notAMember = !get().currentMemberInGroup.has(groupID);
|
|
154
151
|
const isPublic = ((_a = get().currentGroupInfo.get(groupID)) === null || _a === void 0 ? void 0 : _a.visibility) === GroupVisibility.Public;
|
|
155
|
-
if (
|
|
152
|
+
if (notAMember && (!isConfirmedGroup || isPublic)) {
|
|
156
153
|
set({ isViewOnlyMember: true });
|
|
157
154
|
}
|
|
158
155
|
else if (isConfirmedGroup && notAMember) {
|
|
@@ -163,8 +160,9 @@ const useConversationStore = create((set, get) => ({
|
|
|
163
160
|
return;
|
|
164
161
|
}
|
|
165
162
|
}
|
|
166
|
-
//
|
|
167
|
-
// clear here; the Map only ever holds entries keyed by
|
|
163
|
+
// Conversations without a groupID never carry group Map state —
|
|
164
|
+
// nothing to clear here; the Map only ever holds entries keyed by
|
|
165
|
+
// real groupIDs.
|
|
168
166
|
}
|
|
169
167
|
set(() => ({ currentConversation: Object.assign({}, conversation) }));
|
|
170
168
|
},
|