@droppii-org/chat-sdk 0.7.4 → 0.7.6
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/assets/images/defaultAvatarGroup.png +0 -0
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationByGroupItem.js +2 -2
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +3 -3
- package/dist/components/conversation/DeskBackgroundSyncStatus.d.ts +4 -0
- package/dist/components/conversation/DeskBackgroundSyncStatus.d.ts.map +1 -0
- package/dist/components/conversation/DeskBackgroundSyncStatus.js +17 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +13 -3
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +3 -6
- package/dist/components/conversation-detail/PinnedMessagesPanel.d.ts.map +1 -1
- package/dist/components/conversation-detail/PinnedMessagesPanel.js +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
- package/dist/components/conversation-inbox/ConversationPeerAvatar.d.ts.map +1 -1
- package/dist/components/conversation-inbox/ConversationPeerAvatar.js +3 -1
- package/dist/components/conversation-inbox/contact-search/PublicGroupResultItem.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/PublicGroupResultItem.js +3 -2
- package/dist/components/conversation-inbox/contact-search/constants.d.ts +0 -1
- package/dist/components/conversation-inbox/contact-search/constants.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/constants.js +0 -2
- package/dist/components/conversation-inbox/contact-search/index.d.ts +0 -1
- package/dist/components/conversation-inbox/contact-search/index.d.ts.map +1 -1
- package/dist/components/conversation-inbox/contact-search/index.js +0 -1
- package/dist/components/group/GroupAvatar.d.ts +9 -0
- package/dist/components/group/GroupAvatar.d.ts.map +1 -0
- package/dist/components/group/GroupAvatar.js +13 -0
- package/dist/components/group/JoinGroupConfirmModal.d.ts.map +1 -1
- package/dist/components/group/JoinGroupConfirmModal.js +3 -2
- package/dist/components/message/MessageHeader.d.ts.map +1 -1
- package/dist/components/message/MessageHeader.js +2 -2
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +8 -6
- package/dist/components/search-conversation/item/SearchItemAsGroup.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsGroup.js +2 -2
- package/dist/components/search-conversation/item/SearchItemAsMessage.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsMessage.js +3 -1
- package/dist/components/thread/GroupInfoBodyCrm.d.ts +0 -2
- package/dist/components/thread/GroupInfoBodyCrm.d.ts.map +1 -1
- package/dist/components/thread/GroupInfoBodyCrm.js +2 -2
- package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/GroupThreadInfo.js +2 -2
- package/dist/constants/images.d.ts +1 -0
- package/dist/constants/images.d.ts.map +1 -1
- package/dist/constants/images.js +3 -0
- package/dist/hooks/conversation/useResolveConversation.d.ts.map +1 -1
- package/dist/hooks/conversation/useResolveConversation.js +2 -5
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +2 -0
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
- package/dist/hooks/group/useCreateGroup.js +2 -3
- package/dist/hooks/init/useCanLoadChatData.d.ts +7 -0
- package/dist/hooks/init/useCanLoadChatData.d.ts.map +1 -0
- package/dist/hooks/init/useCanLoadChatData.js +15 -0
- package/dist/hooks/message/useCanPinMessage.d.ts +2 -0
- package/dist/hooks/message/useCanPinMessage.d.ts.map +1 -0
- package/dist/hooks/message/useCanPinMessage.js +29 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +3 -5
- package/dist/hooks/message/useUnreadMessageIndicator.d.ts.map +1 -1
- package/dist/hooks/message/useUnreadMessageIndicator.js +3 -5
- package/dist/hooks/session/useAgentSupportThreadJoin.d.ts +1 -0
- package/dist/hooks/session/useAgentSupportThreadJoin.d.ts.map +1 -1
- package/dist/hooks/session/useAgentSupportThreadJoin.js +13 -2
- package/dist/hooks/session/useSessionLatestPreview.d.ts +4 -1
- package/dist/hooks/session/useSessionLatestPreview.d.ts.map +1 -1
- package/dist/hooks/session/useSessionLatestPreview.js +4 -1
- package/dist/locales/vi/boot.json +3 -2
- package/dist/locales/vi/common.json +1 -0
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +3 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts +3 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts.map +1 -1
- package/dist/screens/desk-message/DeskWorkspace.js +4 -4
- package/dist/screens/desk-message/index.d.ts +2 -0
- package/dist/screens/desk-message/index.d.ts.map +1 -1
- package/dist/screens/desk-message/index.js +26 -8
- package/dist/store/boot.d.ts +15 -0
- package/dist/store/boot.d.ts.map +1 -1
- package/dist/store/boot.js +20 -2
- package/dist/styles/global.css +1 -1
- package/package.json +1 -1
- package/dist/components/conversation-inbox/contact-search/PublicGroupAvatar.d.ts +0 -9
- package/dist/components/conversation-inbox/contact-search/PublicGroupAvatar.d.ts.map +0 -1
- package/dist/components/conversation-inbox/contact-search/PublicGroupAvatar.js +0 -19
- package/dist/components/group/GroupCompositeAvatar.d.ts +0 -11
- package/dist/components/group/GroupCompositeAvatar.d.ts.map +0 -1
- package/dist/components/group/GroupCompositeAvatar.js +0 -87
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"AA0BA,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AASD,QAAA,MAAM,eAAe,GAAI,qBAAqB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"AA0BA,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AASD,QAAA,MAAM,eAAe,GAAI,qBAAqB,oBAAoB,mDAirBjE,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -20,7 +20,7 @@ import AdminPermissionModal from "../../components/group/AdminPermissionModal";
|
|
|
20
20
|
import MemberPermissionPanel from "../../components/group/MemberPermissionPanel";
|
|
21
21
|
import DisableSendPermissionConfirm from "../../components/group/DisableSendPermissionConfirm";
|
|
22
22
|
import InviteLinkPanel from "../../components/group/InviteLinkPanel";
|
|
23
|
-
import
|
|
23
|
+
import GroupAvatar from "../../components/group/GroupAvatar";
|
|
24
24
|
import GroupPanelHeader from "../../components/group/GroupPanelHeader";
|
|
25
25
|
import GroupInfoBodyCrm from "./GroupInfoBodyCrm";
|
|
26
26
|
const GroupThreadInfo = ({ onClose, config }) => {
|
|
@@ -256,7 +256,7 @@ const GroupThreadInfo = ({ onClose, config }) => {
|
|
|
256
256
|
if (!groupID) {
|
|
257
257
|
return null;
|
|
258
258
|
}
|
|
259
|
-
return (_jsxs("div", { className: "h-full w-full bg-white flex flex-col relative overflow-hidden", "data-cp-name": "GroupThreadInfo", children: [isCrm ? (_jsxs(_Fragment, { children: [_jsx(GroupPanelHeader, { title: t("group_info_title"), onClose: onClose }), _jsx(GroupInfoBodyCrm, {
|
|
259
|
+
return (_jsxs("div", { className: "h-full w-full bg-white flex flex-col relative overflow-hidden", "data-cp-name": "GroupThreadInfo", children: [isCrm ? (_jsxs(_Fragment, { children: [_jsx(GroupPanelHeader, { title: t("group_info_title"), onClose: onClose }), _jsx(GroupInfoBodyCrm, { groupName: groupDisplayName, faceURL: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.faceURL, introduction: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction, memberCount: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.memberCount, isPublic: (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.visibility) === GroupVisibility.Public, canEditAvatar: permissions.updateGroupAvatar, canEditName: permissions.updateGroupName, canEditDescription: permissions.updateGroupDescription, canConfigPermissions: permissions.configGroupPermission, showInviteLink: isInviteLinkFeatureEnabled && !isViewOnlyMember, showAllowSend: Boolean(config === null || config === void 0 ? void 0 : config.showAllowSendMessage) && isOwner, isOwner: isOwner, isAdminOrOwner: isAdminOrOwner, isViewOnlyMember: isViewOnlyMember, notifyEnabled: notifyEnabled, isTogglingNotification: isTogglingNotification, allowSendMessage: allowSendMessage, isTogglingSendMessage: isTogglingSendMessage, isAvatarUploading: isAvatarUploading, isSaving: isSavingGroupInfo, editingField: editingField, nameValue: nameValue, descriptionValue: descriptionValue, avatarInputRef: avatarInputRef, onNameChange: setNameValue, onDescriptionChange: setDescriptionValue, onStartEdit: (field) => (field === "name" ? startEditName() : startEditDescription()), onCancelEdit: (field) => (field === "name" ? cancelEditName() : cancelEditDescription()), onSaveEdit: (field) => (field === "name" ? saveName() : saveDescription()), onAvatarClick: handleAvatarClick, onAvatarSelect: handleAvatarSelect, onToggleNotification: handleToggleNotification, onToggleSendMessage: handleToggleSendMessage, onOpenRow: setActiveRow, onConfirm: setConfirmAction })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-4 border-b border-gray-100", children: [_jsx("span", { className: "text-lg font-medium", children: t("thread_info_title") }), _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 rounded-full text-gray-500 hover:bg-gray-100", onClick: onClose, children: _jsx(Icon, { icon: "close-o", size: 20 }) })] }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-4", children: [_jsxs("div", { className: "relative shrink-0", children: [_jsx(GroupAvatar, { faceURL: currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.faceURL, groupName: groupDisplayName, size: 56 }), isAvatarUploading && (_jsx("span", { className: "absolute inset-0 flex items-center justify-center rounded-full bg-black/30", children: _jsx("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent" }) })), permissions.updateGroupAvatar && (_jsxs(_Fragment, { children: [_jsx("input", { ref: avatarInputRef, type: "file", accept: ACCEPTED_IMAGE_TYPES.join(", "), className: "hidden", onChange: handleAvatarSelect }), _jsx("button", { type: "button", disabled: isAvatarUploading, onClick: handleAvatarClick, className: "absolute -bottom-1 -right-1 flex items-center justify-center w-5 h-5 rounded-full bg-black text-white", children: _jsx(Icon, { icon: "camera-o", size: 12 }) })] }))] }), _jsx("div", { className: "flex-1 min-w-0", children: editingField !== "name" && (_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("p", { className: "text-base font-medium truncate", children: groupDisplayName }), permissions.updateGroupName && (_jsx(Icon, { icon: "edit-o", size: 16, className: "shrink-0 cursor-pointer text-gray-500", onClick: startEditName }))] })) })] }), editingField === "name" && (_jsx("div", { className: "px-4 pb-4", children: _jsxs("div", { className: "flex flex-col gap-3 bg-[#f5f5f5] rounded-lg p-3", children: [_jsx(Input, { autoFocus: true, value: nameValue, onChange: (event) => setNameValue(event.target.value), maxLength: 255, showCount: true, disabled: isSavingGroupInfo, className: "bg-white" }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg border border-[var(--dc-color-primary-hover)] text-[var(--dc-color-primary-hover)] text-sm font-medium disabled:border-gray-200 disabled:text-gray-300", disabled: isSavingGroupInfo, onClick: cancelEditName, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg bg-[var(--dc-color-primary-hover)] text-white text-sm font-medium disabled:bg-gray-100 disabled:text-gray-300", disabled: isSavingGroupInfo ||
|
|
260
260
|
!nameValue.trim() ||
|
|
261
261
|
nameValue.trim() === ((_c = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupName) !== null && _c !== void 0 ? _c : ""), onClick: saveName, children: t("save") })] })] }) })), _jsx("div", { className: "px-4 pb-4", children: editingField === "description" ? (_jsxs("div", { className: "flex flex-col gap-3 bg-[#f5f5f5] rounded-lg p-3", children: [_jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-gray-900", children: [_jsx(Icon, { icon: "info-circle-o", size: 18, className: "text-gray-500" }), t("description")] }), _jsx(Input.TextArea, { ref: descriptionInputRef, value: descriptionValue, onChange: (event) => setDescriptionValue(event.target.value), placeholder: t("enter_group_description"), autoSize: { minRows: 1, maxRows: 4 }, maxLength: 500, disabled: isSavingGroupInfo, className: "bg-white" }), _jsxs("span", { className: "self-end text-xs text-gray-400", children: [descriptionValue.length, " / 500"] }), _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg border border-[var(--dc-color-primary-hover)] text-[var(--dc-color-primary-hover)] text-sm font-medium disabled:border-gray-200 disabled:text-gray-300", disabled: isSavingGroupInfo, onClick: cancelEditDescription, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-lg bg-[var(--dc-color-primary-hover)] text-white text-sm font-medium disabled:bg-gray-100 disabled:text-gray-300", disabled: isSavingGroupInfo ||
|
|
262
262
|
descriptionValue.trim() === ((_d = currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) !== null && _d !== void 0 ? _d : ""), onClick: saveDescription, children: t("save") })] })] })) : (_jsxs("div", { className: "flex items-start justify-between gap-2 bg-[#f5f5f5] rounded-lg px-3 py-2.5", children: [_jsxs("div", { className: "flex items-start gap-2 flex-1 min-w-0", children: [_jsx(Icon, { icon: "info-circle-o", size: 18, className: "shrink-0 mt-0.5 text-gray-900" }), _jsxs("div", { className: "flex flex-col gap-0.5 min-w-0", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: t("description") }), _jsx("p", { className: "text-sm text-gray-400 truncate", children: (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.introduction) || t("no_description") })] })] }), permissions.updateGroupDescription && (_jsx(Icon, { icon: "edit-o", size: 16, className: "shrink-0 cursor-pointer text-gray-500", onClick: startEditDescription }))] })) }), _jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 rounded-lg overflow-hidden", children: [_jsxs("div", { className: "flex items-center justify-between px-3 py-3", children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "bell-o", size: 18 }), t("notification")] }), _jsx(Switch, { checked: notifyEnabled, onChange: handleToggleNotification, loading: isTogglingNotification })] }), (config === null || config === void 0 ? void 0 : config.showAllowSendMessage) && isOwner && (_jsxs("div", { className: "flex items-center justify-between px-3 py-3", children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "user-check-o", size: 18 }), t("allow_send_message")] }), _jsx(Switch, { checked: allowSendMessage, onChange: handleToggleSendMessage, loading: isTogglingSendMessage })] })), isAdminOrOwner && (config === null || config === void 0 ? void 0 : config.showMutedChatList) && (_jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("muted-chat-list"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "bell-off-o", size: 18 }), t("muted_chat_list")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] })), isAdminOrOwner && (_jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("block-list"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "user-del-o", size: 18 }), t("block_list")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] }))] }), permissions.configGroupPermission && (_jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 mt-3 rounded-lg overflow-hidden", children: [_jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("member-permission"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "shield-o", size: 18 }), t("member_permission_settings")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] }), _jsxs("button", { type: "button", className: "flex items-center justify-between px-3 py-3 text-left", onClick: () => setActiveRow("admin-permission"), children: [_jsxs("span", { className: "flex items-center gap-3 text-sm", children: [_jsx(Icon, { icon: "shield-done-o", size: 18 }), t("admin_permission_settings")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] })] })), isInviteLinkFeatureEnabled && !isViewOnlyMember && (_jsx("div", { className: "mx-4 mt-3 rounded-lg overflow-hidden bg-[#f5f5f5]", children: _jsxs("button", { type: "button", className: "w-full flex items-center justify-between px-3 py-3 text-left text-sm", onClick: () => setActiveRow("invite-link"), children: [_jsxs("span", { className: "flex items-center gap-3", children: [_jsx(Icon, { icon: "link-alt-o", size: 18 }), t("invite_via_link")] }), _jsx(Icon, { icon: "angle-right-o", size: 16, className: "text-gray-400" })] }) })), _jsxs("div", { className: "flex flex-col gap-1 bg-[#f5f5f5] mx-4 mt-3 mb-4 rounded-lg overflow-hidden", children: [isAdminOrOwner && (_jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("delete"), children: [_jsx(Icon, { icon: "trash-o", size: 18 }), t("delete_conversation")] })), !isViewOnlyMember && (_jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("leave"), children: [_jsx(Icon, { icon: "logout-o", size: 18 }), t("leave_group")] })), isOwner && (_jsxs("button", { type: "button", className: "flex items-center gap-3 px-3 py-3 text-left text-sm text-red-500", onClick: () => setConfirmAction("disband"), children: [_jsx(Icon, { icon: "user-del-o", size: 18 }), t("disband_group")] }))] })] })] })), _jsx(ConfirmModal, { open: confirmAction === "leave", onOk: handleConfirmLeave, onCancel: () => setConfirmAction(null), title: t("leave_group_confirm_title"), okText: t("leave_group"), cancelText: t(isCrm ? "action_cancel" : "cancel"), okType: "danger", confirmLoading: isLeaving, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(ConfirmModal, { open: confirmAction === "disband", onOk: handleConfirmDisband, onCancel: () => setConfirmAction(null), title: t("disband_group_confirm_title"), okText: t("disband_group"), cancelText: t(isCrm ? "action_cancel" : "cancel"), okType: "danger", confirmLoading: isDismissing, children: _jsx("p", { children: t("disband_group_confirm_message") }) }), _jsx(ConfirmModal, { open: confirmAction === "delete", onOk: handleConfirmDelete, onCancel: () => setConfirmAction(null), title: t("delete_conversation_confirm_title"), okText: t("delete_conversation"), cancelText: t(isCrm ? "action_cancel" : "cancel"), okType: "danger", confirmLoading: isDeleting, children: _jsx("p", { children: t("delete_conversation_confirm_message") }) }), _jsx(DisableSendPermissionConfirm, { open: confirmAction === "mute-all", title: t("disable_send_permission_title_all_members"), onConfirm: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/constants/images.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"images.d.ts","sourceRoot":"","sources":["../../src/constants/images.ts"],"names":[],"mappings":"AA8BA,eAAO,MAAM,MAAM;;;;;;;;CAQlB,CAAC"}
|
package/dist/constants/images.js
CHANGED
|
@@ -5,6 +5,8 @@ const emptyResources = new URL("../assets/images/emptyResources.png", import.met
|
|
|
5
5
|
const muteNotificationWarning = new URL("../assets/images/muteNotificationWarning.png", import.meta.url).href;
|
|
6
6
|
const searchEmpty = new URL("../assets/images/searchEmpty.png", import.meta.url).href;
|
|
7
7
|
const stickerInvalid = new URL("../assets/images/stickerInvalid.png", import.meta.url).href;
|
|
8
|
+
// Default avatar for a group without a photo (DROPPII-30928).
|
|
9
|
+
const defaultAvatarGroup = new URL("../assets/images/defaultAvatarGroup.png", import.meta.url).href;
|
|
8
10
|
export const images = {
|
|
9
11
|
conversationBg,
|
|
10
12
|
imageFailed,
|
|
@@ -12,4 +14,5 @@ export const images = {
|
|
|
12
14
|
muteNotificationWarning,
|
|
13
15
|
searchEmpty,
|
|
14
16
|
stickerInvalid,
|
|
17
|
+
defaultAvatarGroup,
|
|
15
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAIjB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"useResolveConversation.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useResolveConversation.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,gBAAgB,EAIjB,MAAM,8BAA8B,CAAC;AAetC,MAAM,WAAW,4BAA4B;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsDD,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAWjC,CAAC;AA+DF,eAAO,MAAM,sBAAsB,GAAI,wDAMpC,4BAA4B;;;CA2J9B,CAAC"}
|
|
@@ -4,8 +4,7 @@ import { GroupStatus, GroupVisibility, SessionType, } from "@droppii-org/wasm-cl
|
|
|
4
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
5
5
|
import { apiInstance } from "../../services/api";
|
|
6
6
|
import { ENDPOINTS } from "../../services/routes";
|
|
7
|
-
import {
|
|
8
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
7
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
9
8
|
import useConversationStore from "../../store/conversation";
|
|
10
9
|
import { getGroupIdFromConversationId, getPeerUserIdFromConversationId, resolveConversationBotId, } from "../../utils/conversation";
|
|
11
10
|
import useAuthStore from "../../store/auth";
|
|
@@ -119,9 +118,7 @@ export const useResolveConversation = ({ conversationId, userId, groupId, botId,
|
|
|
119
118
|
// conversationData looks truthy; only a rename to something else (a newer
|
|
120
119
|
// resolve superseding this one) or an explicit reject below clears it.
|
|
121
120
|
const pendingTargetRef = useRef(null);
|
|
122
|
-
const
|
|
123
|
-
const isSdkReady = connectStatus === ConnectStatus.Connected &&
|
|
124
|
-
syncStatus !== SyncStatus.Loading;
|
|
121
|
+
const isSdkReady = useCanLoadChatData();
|
|
125
122
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
126
123
|
const removedFromGroupModalOpen = useConversationStore((state) => state.removedFromGroupModalOpen);
|
|
127
124
|
const setConversationData = useConversationStore((state) => state.setConversationData);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"useGlobalEvent.d.ts","sourceRoot":"","sources":["../../../src/hooks/global/useGlobalEvent.ts"],"names":[],"mappings":"AAiDA,eAAO,MAAM,cAAc,YAqnB1B,CAAC"}
|
|
@@ -234,7 +234,9 @@ export const useGlobalEvent = () => {
|
|
|
234
234
|
}
|
|
235
235
|
updateConnectStatus(ConnectStatus.Disconnected);
|
|
236
236
|
};
|
|
237
|
+
// data is `reinstalled`: true when this device starts from an empty local DB.
|
|
237
238
|
const syncStartHandler = ({ data }) => {
|
|
239
|
+
useBootStore.getState().markSyncStarted(data === true || data === "true");
|
|
238
240
|
updateSyncStatus(SyncStatus.Loading);
|
|
239
241
|
};
|
|
240
242
|
const syncProgressHandler = ({ data }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAqCD,eAAO,MAAM,cAAc;6DAQ2B,iBAAiB;
|
|
1
|
+
{"version":3,"file":"useCreateGroup.d.ts","sourceRoot":"","sources":["../../../src/hooks/group/useCreateGroup.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAqCD,eAAO,MAAM,cAAc;6DAQ2B,iBAAiB;CA2DtE,CAAC"}
|
|
@@ -56,9 +56,8 @@ export const useCreateGroup = () => {
|
|
|
56
56
|
firstMemberName: firstMember === null || firstMember === void 0 ? void 0 : firstMember.displayName,
|
|
57
57
|
remainingCount: restMembers.length,
|
|
58
58
|
});
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
// a static placeholder (see GroupCompositeAvatar).
|
|
59
|
+
// No picked avatar → leave faceURL empty; GroupAvatar shows the default
|
|
60
|
+
// group image (DROPPII-30928).
|
|
62
61
|
const resolvedFaceURL = faceURL || "";
|
|
63
62
|
try {
|
|
64
63
|
const { data: group } = await DChatSDK.createGroup({
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether threads can be opened and their history read: connected and not mid-sync. A fresh
|
|
3
|
+
* device's first sync counts as done once its conversations are stored, since history then
|
|
4
|
+
* loads on demand while the latest message of each conversation is still being pulled.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useCanLoadChatData: () => boolean;
|
|
7
|
+
//# sourceMappingURL=useCanLoadChatData.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanLoadChatData.d.ts","sourceRoot":"","sources":["../../../src/hooks/init/useCanLoadChatData.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,eAO9B,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
3
|
+
import useBootStore, { selectIsSyncingInBackground } from "../../store/boot";
|
|
4
|
+
import { ConnectStatus, SyncStatus } from "../../types/chat";
|
|
5
|
+
/**
|
|
6
|
+
* Whether threads can be opened and their history read: connected and not mid-sync. A fresh
|
|
7
|
+
* device's first sync counts as done once its conversations are stored, since history then
|
|
8
|
+
* loads on demand while the latest message of each conversation is still being pulled.
|
|
9
|
+
*/
|
|
10
|
+
export const useCanLoadChatData = () => {
|
|
11
|
+
const { connectStatus, syncStatus } = useChatContext();
|
|
12
|
+
const isSyncingInBackground = useBootStore(selectIsSyncingInBackground);
|
|
13
|
+
return (connectStatus === ConnectStatus.Connected &&
|
|
14
|
+
(syncStatus !== SyncStatus.Loading || isSyncingInBackground));
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanPinMessage.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useCanPinMessage.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gBAAgB,eA2B5B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useChatContext } from "../../context/ChatContext";
|
|
2
|
+
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
3
|
+
import { isSupportThreadJoinedHere } from "../../hooks/session/useAgentSupportThreadJoin";
|
|
4
|
+
import { useIsJoinedGroup } from "../../hooks/session/useIsJoinedGroup";
|
|
5
|
+
import useAuthStore from "../../store/auth";
|
|
6
|
+
import useConversationStore from "../../store/conversation";
|
|
7
|
+
import { isGroupChat, isSupportSessionAgentView } from "../../utils/conversation";
|
|
8
|
+
// Pin rule of DChatConversationDetail, shared by its toolbox and pinned panel.
|
|
9
|
+
// Desk's MessageList keeps its own rule, which also checks group membership.
|
|
10
|
+
export const useCanPinMessage = () => {
|
|
11
|
+
const { user, allowPinMessageWithoutPermission } = useChatContext();
|
|
12
|
+
const authUserID = useAuthStore((state) => state.userID);
|
|
13
|
+
const conversation = useConversationStore((state) => state.conversationData);
|
|
14
|
+
const permissions = useGroupPermission(conversation === null || conversation === void 0 ? void 0 : conversation.groupID);
|
|
15
|
+
const isGroup = conversation ? isGroupChat(conversation) : false;
|
|
16
|
+
// An agent on someone else's support thread manages pins only once joined.
|
|
17
|
+
const isAgentSupportThread = !!conversation &&
|
|
18
|
+
!isGroup &&
|
|
19
|
+
isSupportSessionAgentView(conversation, (user === null || user === void 0 ? void 0 : user.userID) || authUserID);
|
|
20
|
+
const { isJoined } = useIsJoinedGroup(isAgentSupportThread ? conversation === null || conversation === void 0 ? void 0 : conversation.groupID : undefined);
|
|
21
|
+
if (isAgentSupportThread) {
|
|
22
|
+
return (!conversation.groupID ||
|
|
23
|
+
isJoined === true ||
|
|
24
|
+
isSupportThreadJoinedHere(conversation.conversationID));
|
|
25
|
+
}
|
|
26
|
+
// Some hosts run pin without their backend returning PIN_MESSAGE yet, so
|
|
27
|
+
// the permission check would hide a feature that is live for them.
|
|
28
|
+
return (!isGroup || permissions.pinMessage || !!allowPinMessageWithoutPermission);
|
|
29
|
+
};
|
|
@@ -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;
|
|
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;AAkBtC,eAAO,MAAM,kBAAkB,eAkB9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,SAAS,WAAW,EACpB,OAAO,OAAO,EACd,eAAe;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5E,eAAe,MAAM,KACpB,OA4BF,CAAC;AAEF,eAAO,MAAM,UAAU,GACrB,iBAAiB,MAAM,EACvB,oBAAoB,MAAM,EAC1B,iBAAc,EACd,uBAAqB,EAGrB,yBAAyB,MAAM,IAAI;;;;;qBAed,WAAW,EAAE;;;;;;qBAAb,WAAW,EAAE;;;;;;;CAoZnC,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,WAAW,SACpB,CAAC;AAChC,eAAO,MAAM,gBAAgB,GAAI,SAAS,WAAW,SACpB,CAAC;AAClC,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,SACf,CAAC;AAEtC,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,EAC1B,YAAY,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,KAC9C,WAAW,GAAG,SAqBhB,CAAC;AAMF,eAAO,MAAM,oBAAoB,GAC/B,aAAa,WAAW,EAAE,EAC1B,SAAS,WAAW,EACpB,WAAW,MAAM,GAAG,MAAM,KACzB,WAAW,GAAG,SAQhB,CAAC;AAmBF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,WAAW,EAAE,EACtB,SAAS,WAAW,EAAE,kBAgBvB,CAAC"}
|
|
@@ -7,9 +7,8 @@ import { useTranslation } from "react-i18next";
|
|
|
7
7
|
import { useLatest, useRequest } from "ahooks";
|
|
8
8
|
import emitter, { emit } from "../../utils/events";
|
|
9
9
|
import isEmpty from "lodash/isEmpty";
|
|
10
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
11
10
|
import useAuthStore from "../../store/auth";
|
|
12
|
-
import {
|
|
11
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
13
12
|
import { GROUP_NOTIFICATION_MESSAGE_TYPES, isGroupNotificationMessageType, isSessionEventMessage, isSystemLogMessageType } from "../../utils/messageLog";
|
|
14
13
|
import { isEventCustomMessage, isRenderableCustomMessage, } from "../../utils/customMessage";
|
|
15
14
|
import { isGroupConversation } from "../../utils/imCommon";
|
|
@@ -60,12 +59,11 @@ export const useMessage = (conversationId, searchClientMsgID, enabled = true, us
|
|
|
60
59
|
// Host-supplied, threaded down from the screen props the same way
|
|
61
60
|
// onCopyMessageLink is. Omit it and the SDK raises its own antd toast.
|
|
62
61
|
onSearchMessageRevoked) => {
|
|
63
|
-
const
|
|
62
|
+
const canLoadChatData = useCanLoadChatData();
|
|
64
63
|
const { t } = useTranslation();
|
|
65
64
|
const userID = useAuthStore((state) => state.userID);
|
|
66
65
|
const canLoad = enabled &&
|
|
67
|
-
|
|
68
|
-
syncStatus !== SyncStatus.Loading &&
|
|
66
|
+
canLoadChatData &&
|
|
69
67
|
!!conversationId &&
|
|
70
68
|
!!userID;
|
|
71
69
|
const [loadState, setLoadState] = useState({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useUnreadMessageIndicator.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useUnreadMessageIndicator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"useUnreadMessageIndicator.d.ts","sourceRoot":"","sources":["../../../src/hooks/message/useUnreadMessageIndicator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAShE,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAoEnD,UAAU,+BAA+B;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,WAAW,EAAE,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,qDAIvC,+BAA+B;;;;;;CA6FjC,CAAC"}
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { useLatest } from "ahooks";
|
|
4
4
|
import { DChatSDK } from "../../constants/sdk";
|
|
5
|
-
import { useChatContext } from "../../context/ChatContext";
|
|
6
5
|
import useAuthStore from "../../store/auth";
|
|
7
6
|
import useConversationStore from "../../store/conversation";
|
|
8
|
-
import {
|
|
7
|
+
import { useCanLoadChatData } from "../../hooks/init/useCanLoadChatData";
|
|
9
8
|
// Mark-as-read waits for this lookup, so a call that never settles must not
|
|
10
9
|
// leave the thread unread.
|
|
11
10
|
export const FIRST_UNREAD_LOOKUP_TIMEOUT_MS = 5000;
|
|
@@ -61,13 +60,12 @@ const isOlderThan = (message, boundary) => message.seq && boundary.seq
|
|
|
61
60
|
*/
|
|
62
61
|
export const useUnreadMessageIndicator = ({ conversationId = "", searchClientMsgID, messageList, }) => {
|
|
63
62
|
var _a;
|
|
64
|
-
const
|
|
63
|
+
const canLoadChatData = useCanLoadChatData();
|
|
65
64
|
const userID = useAuthStore((state) => state.userID);
|
|
66
65
|
const storedConversationId = useConversationStore((state) => { var _a; return (_a = state.conversationData) === null || _a === void 0 ? void 0 : _a.conversationID; });
|
|
67
66
|
// Waiting for the store to hold this conversation also waits for its open
|
|
68
67
|
// target, which some hosts set a moment after the selected id.
|
|
69
|
-
const canLookup =
|
|
70
|
-
syncStatus !== SyncStatus.Loading &&
|
|
68
|
+
const canLookup = canLoadChatData &&
|
|
71
69
|
!!conversationId &&
|
|
72
70
|
!!userID &&
|
|
73
71
|
storedConversationId === conversationId;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ConversationItem } from "@droppii-org/wasm-client-sdk";
|
|
2
|
+
export declare const isSupportThreadJoinedHere: (conversationId?: string) => boolean;
|
|
2
3
|
/**
|
|
3
4
|
* CRM's join gate for the thread detail: an agent viewing a support thread
|
|
4
5
|
* they aren't a member of can't send until they join. Opt-in — only a host
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentSupportThreadJoin.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useAgentSupportThreadJoin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAgentSupportThreadJoin.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useAgentSupportThreadJoin.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,8BAA8B,CAAC;AAatC,eAAO,MAAM,yBAAyB,GAAI,iBAAiB,MAAM,YACF,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GACpC,eAAe,gBAAgB,GAAG,IAAI,EACtC,aAAa,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC;;;;;CA8E1D,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { message } from "antd";
|
|
4
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
5
|
import { useTranslation } from "react-i18next";
|
|
5
6
|
import { CbEvents, } from "@droppii-org/wasm-client-sdk";
|
|
6
7
|
import { DChatSDK } from "../../constants/sdk";
|
|
8
|
+
import { QUERY_KEYS } from "../../services/query";
|
|
7
9
|
import useAuthStore from "../../store/auth";
|
|
8
10
|
import { PeerType } from "../../types/conversation";
|
|
9
11
|
import { isSupportSessionAgentView } from "../../utils/conversation";
|
|
@@ -12,6 +14,7 @@ import { useIsJoinedGroup } from "./useIsJoinedGroup";
|
|
|
12
14
|
// check right after still says "not a member". Joins made in this page
|
|
13
15
|
// session are trusted instead — kept outside React so a remount keeps them.
|
|
14
16
|
const joinedConversationIds = new Set();
|
|
17
|
+
export const isSupportThreadJoinedHere = (conversationId) => !!conversationId && joinedConversationIds.has(conversationId);
|
|
15
18
|
/**
|
|
16
19
|
* CRM's join gate for the thread detail: an agent viewing a support thread
|
|
17
20
|
* they aren't a member of can't send until they join. Opt-in — only a host
|
|
@@ -19,6 +22,7 @@ const joinedConversationIds = new Set();
|
|
|
19
22
|
*/
|
|
20
23
|
export const useAgentSupportThreadJoin = (conversation, joinThread) => {
|
|
21
24
|
const { t } = useTranslation();
|
|
25
|
+
const queryClient = useQueryClient();
|
|
22
26
|
const userID = useAuthStore((state) => state.userID);
|
|
23
27
|
const isCrm = useAuthStore((state) => state.isCrm);
|
|
24
28
|
const isAgentSupportThread = !!joinThread &&
|
|
@@ -68,8 +72,15 @@ export const useAgentSupportThreadJoin = (conversation, joinThread) => {
|
|
|
68
72
|
joinedConversationIds.add(conversationId);
|
|
69
73
|
setJoinCount((count) => count + 1);
|
|
70
74
|
message.success(t("join_group_success"));
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
// Re-renders other readers of this membership (pin rights); a join on a
|
|
76
|
+
// cached group syncs as an update, so no join event would refetch it.
|
|
77
|
+
if (groupId) {
|
|
78
|
+
const queryKey = [QUERY_KEYS.IS_JOINED_GROUP, groupId];
|
|
79
|
+
await queryClient.cancelQueries({ queryKey });
|
|
80
|
+
queryClient.setQueryData(queryKey, true);
|
|
81
|
+
}
|
|
82
|
+
}, [conversationId, groupId, joinThread, queryClient, t]);
|
|
83
|
+
const isJoinedHere = isSupportThreadJoinedHere(conversationId);
|
|
73
84
|
const isGated = !!groupId && !isJoinedHere;
|
|
74
85
|
return {
|
|
75
86
|
isCheckingMembership: isGated && isJoined === null && !isError,
|
|
@@ -7,5 +7,8 @@ export interface ConversationPreviewSource {
|
|
|
7
7
|
conversationId: string;
|
|
8
8
|
conversation?: ConversationItem;
|
|
9
9
|
}
|
|
10
|
-
export declare const useSessionLatestPreview: (sessions: ConversationPreviewSource[]) =>
|
|
10
|
+
export declare const useSessionLatestPreview: (sessions: ConversationPreviewSource[]) => {
|
|
11
|
+
previews: Map<string, LatestPreview>;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
};
|
|
11
14
|
//# sourceMappingURL=useSessionLatestPreview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionLatestPreview.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useSessionLatestPreview.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAyB,MAAM,8BAA8B,CAAC;AASvF,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAKD,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAkBD,eAAO,MAAM,uBAAuB,GAClC,UAAU,yBAAyB,EAAE
|
|
1
|
+
{"version":3,"file":"useSessionLatestPreview.d.ts","sourceRoot":"","sources":["../../../src/hooks/session/useSessionLatestPreview.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAyB,MAAM,8BAA8B,CAAC;AASvF,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAKD,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAkBD,eAAO,MAAM,uBAAuB,GAClC,UAAU,yBAAyB,EAAE;;;CA8EtC,CAAC"}
|
|
@@ -54,7 +54,9 @@ export const useSessionLatestPreview = (sessions) => {
|
|
|
54
54
|
emitter.off("GROUP_MEMBERS_CHANGED", groupMembersChangedHandler);
|
|
55
55
|
};
|
|
56
56
|
}, [sessions, queryClient]);
|
|
57
|
-
|
|
57
|
+
// A preview is loading only while its query fetches for the first time.
|
|
58
|
+
const isLoading = results.some((result) => result.isLoading);
|
|
59
|
+
const previews = useMemo(() => {
|
|
58
60
|
const map = new Map();
|
|
59
61
|
sessions.forEach((session, index) => {
|
|
60
62
|
var _a, _b, _c;
|
|
@@ -77,4 +79,5 @@ export const useSessionLatestPreview = (sessions) => {
|
|
|
77
79
|
});
|
|
78
80
|
return map;
|
|
79
81
|
}, [sessions, results]);
|
|
82
|
+
return { previews, isLoading };
|
|
80
83
|
};
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
"sync": "Đồng bộ tin nhắn"
|
|
13
13
|
},
|
|
14
14
|
"slow": "Mạng đang chậm hơn bình thường. Livechat vẫn đang tải, bạn không cần làm gì thêm.",
|
|
15
|
-
"error_hint": "Kiểm tra kết nối mạng rồi bấm Thử lại. Nếu vẫn lỗi, báo bộ phận kỹ thuật: dừng ở bước {{step}}."
|
|
15
|
+
"error_hint": "Kiểm tra kết nối mạng rồi bấm Thử lại. Nếu vẫn lỗi, báo bộ phận kỹ thuật: dừng ở bước {{step}}.",
|
|
16
|
+
"background_sync": "Đang đồng bộ các cuộc trò chuyện còn lại"
|
|
16
17
|
},
|
|
17
18
|
"inbox": {
|
|
18
19
|
"title": "Đang tải tin nhắn",
|
|
19
20
|
"engine": "Đang chuẩn bị hộp thư…",
|
|
20
21
|
"connect": "Đang kết nối…",
|
|
21
|
-
"sync": "
|
|
22
|
+
"sync": "Vui lòng chờ trong giây lát…",
|
|
22
23
|
"slow": "Lần đầu mở có thể mất một chút thời gian. Những lần sau sẽ nhanh hơn.",
|
|
23
24
|
"error_title": "Chưa tải được tin nhắn",
|
|
24
25
|
"error_hint": "Kiểm tra kết nối mạng rồi thử lại."
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"context_menu_view_original": "Xem tin nhắn gốc",
|
|
75
75
|
"pinned_panel_title": "Tin nhắn đã ghim",
|
|
76
76
|
"pinned_panel_hint": "Nhấn giữ tin nhắn để chọn \"Bỏ ghim\" hoặc \"Xem tin nhắn gốc\"",
|
|
77
|
+
"pinned_panel_hint_view_only": "Nhấn giữ tin nhắn để chọn \"Xem tin nhắn gốc\"",
|
|
77
78
|
"pin_message_success": "Đã ghim tin nhắn",
|
|
78
79
|
"pin_message_failed": "Ghim tin nhắn thất bại",
|
|
79
80
|
"unpin_message_success": "Đã bỏ ghim tin nhắn",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation-detail/index.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAYrF,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,6CAA6C,CAAC;AAQrD,MAAM,WAAW,4BACf,SAAQ,4BAA4B,EAClC,4BAA4B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;CACrC;AAED,QAAA,MAAM,uBAAuB,GAAI,8IAc9B,4BAA4B,mDA6Q9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -28,6 +28,7 @@ import { ConversationMessageList, ConversationDetailHeader, ConversationDetailIn
|
|
|
28
28
|
import TypingIndicatorBar from "../../components/conversation-detail/TypingIndicatorBar";
|
|
29
29
|
import { BotButtonMessage, BotInactiveNotice, BotMenuSheet, BotStartButton, BotSuggest, CsSessionConfirmModal, } from "../../components/conversation-detail/bot";
|
|
30
30
|
import { useBotFlow } from "../../hooks/conversation/useBotFlow";
|
|
31
|
+
import { useCanPinMessage } from "../../hooks/message/useCanPinMessage";
|
|
31
32
|
import { BOT_TYPE } from "../../constants/botFlow";
|
|
32
33
|
import { useResolveConversation, } from "../../hooks/conversation/useResolveConversation";
|
|
33
34
|
import { nextConsultantReturnTarget, useConsultantChatNavigation, } from "../../hooks/conversation/useConsultantChatNavigation";
|
|
@@ -45,6 +46,7 @@ const DChatConversationDetail = (_a) => {
|
|
|
45
46
|
const conversation = useConversationStore((state) => state.conversationData);
|
|
46
47
|
const authUserID = useAuthStore((state) => state.userID);
|
|
47
48
|
const isViewOnlyMember = useConversationStore((state) => state.isViewOnlyMember);
|
|
49
|
+
const canPinMessage = useCanPinMessage();
|
|
48
50
|
const clearSelectedConversation = useConversationStore((state) => state.clearSelectedConversation);
|
|
49
51
|
const setPendingResolveTarget = useConversationStore((state) => state.setPendingResolveTarget);
|
|
50
52
|
const { isResolving, error } = useResolveConversation({
|
|
@@ -139,6 +141,6 @@ const DChatConversationDetail = (_a) => {
|
|
|
139
141
|
!botFlow.flowIsEnd &&
|
|
140
142
|
botFlow.shouldShowButtons &&
|
|
141
143
|
botFlow.currentButtonsBlock ? (_jsx(BotButtonMessage, { buttons: botFlow.currentButtonsBlock.data.buttons, disabled: botFlow.isProcessing, onSelect: botFlow.selectButton, onRestart: botFlow.restart })) : null;
|
|
142
|
-
return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile }), _jsx(PinnedMessageBar, {}), _jsxs("div", { [COMPOSER_DISMISS_ZONE_ATTR]: "", className: "relative flex min-h-0 flex-1 flex-col", children: [children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })), _jsx(TypingIndicatorBar, { conversationId: conversation.conversationID })] }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab }), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
|
|
144
|
+
return (_jsxs("div", { "data-testid": "conversation-detail-root", className: clsx("relative flex h-full w-full flex-col overflow-hidden bg-white", className), children: [_jsx(ConversationDetailHeader, { conversation: conversation, onBack: handleBack, onProfileClick: openProfile }), _jsx(PinnedMessageBar, { canUnpin: canPinMessage }), _jsxs("div", { [COMPOSER_DISMISS_ZONE_ATTR]: "", className: "relative flex min-h-0 flex-1 flex-col", children: [children !== null && children !== void 0 ? children : (_jsx(ConversationMessageList, { bottomSlot: botButtonsSlot, onCopyMessageLink: onCopyMessageLink, onSearchMessageRevoked: onSearchMessageRevoked })), _jsx(TypingIndicatorBar, { conversationId: conversation.conversationID })] }), footer, isBotThread && (_jsxs(_Fragment, { children: [_jsx(BotMenuSheet, { open: isMenuOpen, items: botFlow.menuItems, disabled: botFlow.isProcessing, onClose: closeMenu, onSelect: botFlow.selectMenuOption }), _jsx(CsSessionConfirmModal, { open: botFlow.isCsConfirmOpen, loading: botFlow.isProcessing, onCancel: botFlow.cancelMenuOption, onConfirm: botFlow.confirmEndCsSession })] })), _jsx(ThreadProfilePanel, { open: isProfileOpen, conversation: conversation, onClose: closeProfile, onSelectResource: setResourceTab }), _jsx(MediaResourcePanel, { open: resourceTab !== null, initialTab: resourceTab !== null && resourceTab !== void 0 ? resourceTab : MediaResourceTabKey.Image, onClose: () => setResourceTab(null) })] }));
|
|
143
145
|
};
|
|
144
146
|
export default DChatConversationDetail;
|
|
@@ -11,8 +11,10 @@ interface DeskWorkspaceProps {
|
|
|
11
11
|
isOffline?: boolean;
|
|
12
12
|
/** Shown in the offline overlay: reconnect status, and Retry once the reconnect has failed. */
|
|
13
13
|
offlineContent?: ReactNode;
|
|
14
|
+
/** First boot: covers the desk (locked like offline) while its first screen of data loads. */
|
|
15
|
+
bootOverlay?: ReactNode;
|
|
14
16
|
}
|
|
15
17
|
/** Bright Workspace: folder, list, chat and info as separate white panels on the page canvas. */
|
|
16
|
-
declare const DeskWorkspace: ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMessageLink, onSearchMessageRevoked, onOpenAppMenu, isOffline, offlineContent, }: DeskWorkspaceProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
declare const DeskWorkspace: ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMessageLink, onSearchMessageRevoked, onOpenAppMenu, isOffline, offlineContent, bootOverlay, }: DeskWorkspaceProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
19
|
export default DeskWorkspace;
|
|
18
20
|
//# sourceMappingURL=DeskWorkspace.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeskWorkspace.d.ts","sourceRoot":"","sources":["../../../src/screens/desk-message/DeskWorkspace.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"DeskWorkspace.d.ts","sourceRoot":"","sources":["../../../src/screens/desk-message/DeskWorkspace.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAqB5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,UAAU,kBAAkB;IAC1B,qBAAqB,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACrD,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,8FAA8F;IAC9F,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB;AAID,iGAAiG;AACjG,QAAA,MAAM,aAAa,GAAI,qJASpB,kBAAkB,4CA2LpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -10,7 +10,7 @@ import { useMediaQuery } from "../../hooks/common/useMediaQuery";
|
|
|
10
10
|
import { useSidePanel } from "../../hooks/common/useSidePanel";
|
|
11
11
|
import { useVisualViewportHeight } from "../../hooks/common/useVisualViewportHeight";
|
|
12
12
|
import { useViewportTier } from "../../hooks/common/useViewportTier";
|
|
13
|
-
import { useDeskOfflineLock } from "../../hooks/conversation/useDeskOfflineLock";
|
|
13
|
+
import { DESK_OFFLINE_OVERLAY_ATTR, useDeskOfflineLock, } from "../../hooks/conversation/useDeskOfflineLock";
|
|
14
14
|
import { useDeskThreadNavigation } from "../../hooks/conversation/useDeskThreadNavigation";
|
|
15
15
|
import useSidePanelStore from "../../store/sidePanel";
|
|
16
16
|
import useConversationStore from "../../store/conversation";
|
|
@@ -26,7 +26,7 @@ import DeskOfflineOverlay from "./DeskOfflineOverlay";
|
|
|
26
26
|
import { PeerType } from "../../types/conversation";
|
|
27
27
|
const minWidth = (px) => `(min-width: ${px}px)`;
|
|
28
28
|
/** Bright Workspace: folder, list, chat and info as separate white panels on the page canvas. */
|
|
29
|
-
const DeskWorkspace = ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMessageLink, onSearchMessageRevoked, onOpenAppMenu, isOffline = false, offlineContent, }) => {
|
|
29
|
+
const DeskWorkspace = ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMessageLink, onSearchMessageRevoked, onOpenAppMenu, isOffline = false, offlineContent, bootOverlay, }) => {
|
|
30
30
|
const selectedConversationId = useConversationStore((state) => state.selectedConversationId);
|
|
31
31
|
const searchClientMsgID = useConversationStore((state) => state.searchClientMsgID);
|
|
32
32
|
const { isOpen: isInfoOpen, toggle: toggleInfo, close: closeInfo } = useSidePanel("info");
|
|
@@ -78,7 +78,7 @@ const DeskWorkspace = ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMes
|
|
|
78
78
|
useSidePanelStore.getState().closePanel();
|
|
79
79
|
}, [isPhone, selectedConversationId]);
|
|
80
80
|
const wasShowingThread = useRef(showThread);
|
|
81
|
-
useDeskOfflineLock(workspaceEl, isOffline, () => {
|
|
81
|
+
useDeskOfflineLock(workspaceEl, isOffline || Boolean(bootOverlay), () => {
|
|
82
82
|
var _a, _b;
|
|
83
83
|
return isPhone
|
|
84
84
|
? showThread
|
|
@@ -121,6 +121,6 @@ const DeskWorkspace = ({ openCreateCannedModal, groupThreadInfoConfig, onCopyMes
|
|
|
121
121
|
? undefined
|
|
122
122
|
: { width: isFolderInline ? LAYOUT_WIDTHS.list : LAYOUT_WIDTHS.listCompact }, children: [_jsx(DeskConversationList, { onOpenAppMenu: onOpenAppMenu }), !isFolderInline && _jsx(FolderOverlay, {})] }), _jsxs("div", { ref: chatPaneRef, className: isPhone
|
|
123
123
|
? `absolute inset-0 flex flex-col overflow-hidden bg-white ${showThread ? "" : "invisible"}`
|
|
124
|
-
: "flex h-full flex-1 overflow-hidden rounded-xl bg-white shadow-sm", style: isPhone ? undefined : { minWidth: isDesktop ? LAYOUT_WIDTHS.chatMin : 0 }, children: [isPhone && !hasConversationData && (_jsx(PhoneThreadFallbackHeader, { onBack: backToList, onOpenAppMenu: onOpenAppMenu })), messageList] }), _jsx(ThreadInfo, { isOpen: isInfoOpen, onClose: closeInfo, groupConfig: groupThreadInfoConfig, supportPanelInline: isSupportPanelInline, drawerContainer: workspaceEl }), _jsx("div", { ref: setPanelSlotEl, className: "contents" }), _jsx(ShortcutHelpModal, { open: isHelpOpen, onClose: () => setIsHelpOpen(false) }), isOffline && (_jsx(DeskOfflineOverlay, { isPhone: isPhone, onOpenAppMenu: onOpenAppMenu, children: offlineContent }))] }));
|
|
124
|
+
: "flex h-full flex-1 overflow-hidden rounded-xl bg-white shadow-sm", style: isPhone ? undefined : { minWidth: isDesktop ? LAYOUT_WIDTHS.chatMin : 0 }, children: [isPhone && !hasConversationData && (_jsx(PhoneThreadFallbackHeader, { onBack: backToList, onOpenAppMenu: onOpenAppMenu })), messageList] }), _jsx(ThreadInfo, { isOpen: isInfoOpen, onClose: closeInfo, groupConfig: groupThreadInfoConfig, supportPanelInline: isSupportPanelInline, drawerContainer: workspaceEl }), _jsx("div", { ref: setPanelSlotEl, className: "contents" }), _jsx(ShortcutHelpModal, { open: isHelpOpen, onClose: () => setIsHelpOpen(false) }), bootOverlay ? (_jsx("div", { [DESK_OFFLINE_OVERLAY_ATTR]: "", "data-testid": "desk-boot-overlay", className: "absolute inset-0 z-[1000] flex", children: bootOverlay })) : (isOffline && (_jsx(DeskOfflineOverlay, { isPhone: isPhone, onOpenAppMenu: onOpenAppMenu, children: offlineContent })))] }));
|
|
125
125
|
};
|
|
126
126
|
export default DeskWorkspace;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { GroupThreadInfoConfig } from "../../components/thread/GroupThreadInfo";
|
|
2
2
|
import type { CannedCreateRequest } from "../../types/chat";
|
|
3
|
+
/** Longest the boot screen waits for the desk's first page of conversations once it can load. */
|
|
4
|
+
export declare const FIRST_SCREEN_MAX_WAIT_MS = 10000;
|
|
3
5
|
interface DChatDeskMessageProps {
|
|
4
6
|
openCreateCannedModal?: (request?: CannedCreateRequest) => void;
|
|
5
7
|
groupThreadInfoConfig?: GroupThreadInfoConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/desk-message/index.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/screens/desk-message/index.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAM5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGxD,iGAAiG;AACjG,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,UAAU,qBAAqB;IAC7B,qBAAqB,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAChE,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,iBAAiB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1E,kGAAkG;IAClG,sBAAsB,CAAC,EAAE,MAAM,IAAI,CAAC;IACpC,+GAA+G;IAC/G,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CACtD;AAWD,QAAA,MAAM,gBAAgB,GAAI,OAAO,qBAAqB,4CA4DrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -6,10 +6,13 @@ import { useChatContext } from "../../context/ChatContext";
|
|
|
6
6
|
import { ConnectStatus } from "../../types/chat";
|
|
7
7
|
import { QUERY_KEYS } from "../../services/query";
|
|
8
8
|
import { useChatBootPhase } from "../../hooks/init/useChatBootPhase";
|
|
9
|
+
import useBootStore, { selectHasConversations } from "../../store/boot";
|
|
9
10
|
import DeskWorkspace from "./DeskWorkspace";
|
|
10
11
|
import DeskWorkspaceSkeleton from "./DeskWorkspaceSkeleton";
|
|
11
12
|
import DeskBootCard from "./DeskBootCard";
|
|
12
13
|
const DESK_SLOW_AFTER_MS = 15000;
|
|
14
|
+
/** Longest the boot screen waits for the desk's first page of conversations once it can load. */
|
|
15
|
+
export const FIRST_SCREEN_MAX_WAIT_MS = 10000;
|
|
13
16
|
// Lists behind the desk; they refetch after a reconnect because events were missed while offline.
|
|
14
17
|
const DESK_QUERY_KEYS = [
|
|
15
18
|
QUERY_KEYS.GET_TEAM_SESSIONS,
|
|
@@ -23,13 +26,26 @@ const DChatDeskMessage = (props) => {
|
|
|
23
26
|
const { connectStatus } = useChatContext();
|
|
24
27
|
const queryClient = useQueryClient();
|
|
25
28
|
const isConnected = connectStatus === ConnectStatus.Connected;
|
|
26
|
-
|
|
27
|
-
const
|
|
29
|
+
const hasConversations = useBootStore(selectHasConversations);
|
|
30
|
+
const isFirstScreenReady = useBootStore((state) => state.isFirstScreenReady);
|
|
31
|
+
// The desk mounts once the local DB holds the conversations; a fresh device's sync then
|
|
32
|
+
// finishes in the background while the first page loads on demand under the boot screen.
|
|
33
|
+
const canMountDesk = isConnected && hasConversations;
|
|
34
|
+
const [isFirstScreenTimedOut, setIsFirstScreenTimedOut] = useState(false);
|
|
35
|
+
const isBooted = canMountDesk && (isFirstScreenReady || isFirstScreenTimedOut);
|
|
36
|
+
// After the first screen has shown, the desk stays mounted, so a reconnect keeps scroll, search and panels.
|
|
37
|
+
const [hasBooted, setHasBooted] = useState(isBooted);
|
|
28
38
|
const wasDropped = useRef(false);
|
|
29
39
|
useEffect(() => {
|
|
30
|
-
if (
|
|
40
|
+
if (isBooted)
|
|
31
41
|
setHasBooted(true);
|
|
32
|
-
}, [
|
|
42
|
+
}, [isBooted]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (hasBooted || !canMountDesk || isFirstScreenReady)
|
|
45
|
+
return;
|
|
46
|
+
const timer = setTimeout(() => setIsFirstScreenTimedOut(true), FIRST_SCREEN_MAX_WAIT_MS);
|
|
47
|
+
return () => clearTimeout(timer);
|
|
48
|
+
}, [hasBooted, canMountDesk, isFirstScreenReady]);
|
|
33
49
|
useEffect(() => {
|
|
34
50
|
if (!hasBooted)
|
|
35
51
|
return;
|
|
@@ -47,9 +63,11 @@ const DChatDeskMessage = (props) => {
|
|
|
47
63
|
},
|
|
48
64
|
});
|
|
49
65
|
}, [hasBooted, isConnected, queryClient]);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
66
|
+
const showDesk = hasBooted || isBooted;
|
|
67
|
+
// An incremental sync can end before the first page is in; keep the sync step on screen till then.
|
|
68
|
+
const bootScreen = (_jsx(DeskWorkspaceSkeleton, { children: _jsx(DeskBootCard, { boot: boot.phase === "ready" ? Object.assign(Object.assign({}, boot), { phase: "sync" }) : boot }) }));
|
|
69
|
+
if (!showDesk && !canMountDesk)
|
|
70
|
+
return bootScreen;
|
|
71
|
+
return (_jsx(DeskWorkspace, Object.assign({}, props, { isOffline: !isConnected, offlineContent: isConnected ? undefined : _jsx(DeskBootCard, { boot: boot }), bootOverlay: showDesk ? undefined : bootScreen })));
|
|
54
72
|
};
|
|
55
73
|
export default DChatDeskMessage;
|