@droppii-org/chat-sdk 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/images/defaultAvatarGroup.png +0 -0
- package/dist/components/boot/BootProgressBar.d.ts +13 -0
- package/dist/components/boot/BootProgressBar.d.ts.map +1 -0
- package/dist/components/boot/BootProgressBar.js +18 -0
- package/dist/components/canned-response/CannedResponsePanel.d.ts.map +1 -1
- package/dist/components/canned-response/CannedResponsePanel.js +2 -2
- package/dist/components/common/BottomSheet.d.ts +18 -0
- package/dist/components/common/BottomSheet.d.ts.map +1 -0
- package/dist/components/common/BottomSheet.js +49 -0
- package/dist/components/common/DeskPanelDrawer.d.ts.map +1 -1
- package/dist/components/common/DeskPanelDrawer.js +29 -15
- package/dist/components/common/DeskSidePanel.d.ts.map +1 -1
- package/dist/components/common/DeskSidePanel.js +1 -1
- package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
- package/dist/components/compose/ComposeNewMessageModal.js +3 -3
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationByGroupItem.js +5 -10
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +6 -11
- package/dist/components/conversation/DeskConversationList.d.ts +4 -1
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +2 -2
- package/dist/components/conversation/DeskListHeader.d.ts +2 -0
- package/dist/components/conversation/DeskListHeader.d.ts.map +1 -1
- package/dist/components/conversation/DeskListHeader.js +3 -3
- package/dist/components/conversation-detail/ConversationMessageList.d.ts.map +1 -1
- package/dist/components/conversation-detail/ConversationMessageList.js +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
- package/dist/components/conversation-inbox/ConversationInboxList.js +12 -3
- 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/InboxBootStatus.d.ts +10 -0
- package/dist/components/conversation-inbox/InboxBootStatus.d.ts.map +1 -0
- package/dist/components/conversation-inbox/InboxBootStatus.js +34 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.d.ts +6 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.d.ts.map +1 -0
- package/dist/components/conversation-inbox/InboxListSkeleton.js +8 -0
- 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/GroupPanelHeader.js +1 -1
- package/dist/components/group/JoinGroupConfirmModal.d.ts.map +1 -1
- package/dist/components/group/JoinGroupConfirmModal.js +3 -2
- package/dist/components/icon/strokeIconNodes.d.ts +6 -0
- package/dist/components/icon/strokeIconNodes.d.ts.map +1 -1
- package/dist/components/icon/strokeIconNodes.js +5 -0
- package/dist/components/message/MessageHeader.d.ts +3 -1
- package/dist/components/message/MessageHeader.d.ts.map +1 -1
- package/dist/components/message/MessageHeader.js +11 -7
- package/dist/components/message/MessageList.d.ts +2 -0
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +15 -4
- package/dist/components/message/SelectSession.js +1 -1
- package/dist/components/message/footer/ActionBar.d.ts.map +1 -1
- package/dist/components/message/footer/ActionBar.js +2 -2
- package/dist/components/message/footer/ComposerTabs.js +1 -1
- package/dist/components/message/footer/CrmComposerCard.js +1 -1
- package/dist/components/message/footer/EmojiPicker.d.ts +3 -1
- package/dist/components/message/footer/EmojiPicker.d.ts.map +1 -1
- package/dist/components/message/footer/EmojiPicker.js +3 -2
- package/dist/components/message/footer/EnterHandler.d.ts.map +1 -1
- package/dist/components/message/footer/EnterHandler.js +16 -2
- package/dist/components/message/footer/MediaActions.d.ts +2 -1
- package/dist/components/message/footer/MediaActions.d.ts.map +1 -1
- package/dist/components/message/footer/MediaActions.js +4 -4
- package/dist/components/message/footer/ToolbarPlugin.js +1 -1
- package/dist/components/message/footer/mention/MentionMenu.js +1 -1
- package/dist/components/message/headerIconButton.js +1 -1
- package/dist/components/message/item/MessageMoreButton.d.ts.map +1 -1
- package/dist/components/message/item/MessageMoreButton.js +18 -5
- package/dist/components/message/item/QuoteMessage.d.ts.map +1 -1
- package/dist/components/message/item/QuoteMessage.js +3 -50
- package/dist/components/message/reply/ReplyPreviewBar.d.ts.map +1 -1
- package/dist/components/message/reply/ReplyPreviewBar.js +4 -13
- package/dist/components/rich-text-editor/RichTextEditor.d.ts +3 -1
- package/dist/components/rich-text-editor/RichTextEditor.d.ts.map +1 -1
- package/dist/components/rich-text-editor/RichTextEditor.js +4 -2
- package/dist/components/search-conversation/item/SearchItemAsGroup.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsGroup.js +5 -5
- package/dist/components/search-conversation/item/SearchItemAsMessage.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsMessage.js +13 -1
- package/dist/components/search-conversation/item/SearchItemAsUser.d.ts.map +1 -1
- package/dist/components/search-conversation/item/SearchItemAsUser.js +6 -0
- package/dist/components/session/DeskAssignedSession.js +2 -2
- package/dist/components/session/FolderRow.js +1 -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/components/thread/ManualAssignPopover.d.ts.map +1 -1
- package/dist/components/thread/ManualAssignPopover.js +13 -6
- package/dist/components/thread/SessionDetailCard.js +1 -1
- package/dist/components/thread/SessionTagPicker.js +1 -1
- package/dist/components/thread/SupportSessionPanel.d.ts +1 -1
- package/dist/components/thread/SupportSessionPanel.d.ts.map +1 -1
- package/dist/components/thread/SupportSessionPanel.js +1 -1
- package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/ThreadInfo.js +3 -1
- package/dist/components/thread/ThreadInfoHeader.d.ts +1 -1
- package/dist/components/thread/ThreadInfoHeader.d.ts.map +1 -1
- package/dist/components/thread/ThreadInfoHeader.js +1 -1
- package/dist/components/thread/UserSection.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/constants/layout.d.ts +10 -1
- package/dist/constants/layout.d.ts.map +1 -1
- package/dist/constants/layout.js +10 -1
- package/dist/hooks/common/useDeskShortcuts.d.ts.map +1 -1
- package/dist/hooks/common/useDeskShortcuts.js +3 -1
- package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts +3 -0
- package/dist/hooks/common/useIsDeskWorkspaceMounted.d.ts.map +1 -0
- package/dist/hooks/common/useIsDeskWorkspaceMounted.js +4 -0
- package/dist/hooks/common/useViewportTier.d.ts +4 -0
- package/dist/hooks/common/useViewportTier.d.ts.map +1 -0
- package/dist/hooks/common/useViewportTier.js +9 -0
- package/dist/hooks/common/useVisualViewportHeight.d.ts +10 -0
- package/dist/hooks/common/useVisualViewportHeight.d.ts.map +1 -0
- package/dist/hooks/common/useVisualViewportHeight.js +30 -0
- package/dist/hooks/conversation/useConversationList.d.ts.map +1 -1
- package/dist/hooks/conversation/useConversationList.js +31 -0
- package/dist/hooks/conversation/useDeskOfflineLock.d.ts +9 -0
- package/dist/hooks/conversation/useDeskOfflineLock.d.ts.map +1 -0
- package/dist/hooks/conversation/useDeskOfflineLock.js +46 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.d.ts +19 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.d.ts.map +1 -0
- package/dist/hooks/conversation/useDeskThreadNavigation.js +68 -0
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +22 -2
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
- package/dist/hooks/group/useCreateGroup.js +6 -7
- package/dist/hooks/init/useChatBootPhase.d.ts +30 -0
- package/dist/hooks/init/useChatBootPhase.d.ts.map +1 -0
- package/dist/hooks/init/useChatBootPhase.js +72 -0
- package/dist/hooks/message/usePinMessage.d.ts +0 -2
- package/dist/hooks/message/usePinMessage.d.ts.map +1 -1
- package/dist/hooks/message/usePinMessage.js +7 -11
- package/dist/hooks/message/useQuotedSenderName.d.ts +14 -0
- package/dist/hooks/message/useQuotedSenderName.d.ts.map +1 -0
- package/dist/hooks/message/useQuotedSenderName.js +41 -0
- package/dist/hooks/user/useAuth.d.ts.map +1 -1
- package/dist/hooks/user/useAuth.js +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/locales/i18n.d.ts.map +1 -1
- package/dist/locales/i18n.js +3 -2
- package/dist/locales/i18n.ts +3 -2
- package/dist/locales/vi/boot.json +28 -0
- package/dist/locales/vi/common.json +1 -1
- package/dist/screens/conversation-detail/index.d.ts.map +1 -1
- package/dist/screens/conversation-detail/index.js +8 -3
- package/dist/screens/desk-message/DeskBootCard.d.ts +7 -0
- package/dist/screens/desk-message/DeskBootCard.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskBootCard.js +69 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.d.ts +10 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskOfflineOverlay.js +11 -0
- package/dist/screens/desk-message/DeskWorkspace.d.ts +7 -1
- package/dist/screens/desk-message/DeskWorkspace.d.ts.map +1 -1
- package/dist/screens/desk-message/DeskWorkspace.js +82 -13
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts +10 -0
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.d.ts.map +1 -0
- package/dist/screens/desk-message/DeskWorkspaceSkeleton.js +18 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts +8 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.d.ts.map +1 -0
- package/dist/screens/desk-message/PhoneThreadFallbackHeader.js +11 -0
- 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 +47 -3
- package/dist/store/boot.d.ts +29 -0
- package/dist/store/boot.d.ts.map +1 -0
- package/dist/store/boot.js +21 -0
- package/dist/store/conversation.d.ts +2 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +13 -12
- package/dist/store/deskPanelHost.d.ts +2 -0
- package/dist/store/deskPanelHost.d.ts.map +1 -1
- package/dist/store/deskPanelHost.js +1 -0
- package/dist/store/type.d.ts +4 -1
- package/dist/store/type.d.ts.map +1 -1
- package/dist/store/workspaceLayout.d.ts +3 -0
- package/dist/store/workspaceLayout.d.ts.map +1 -1
- package/dist/store/workspaceLayout.js +2 -0
- package/dist/styles/global.css +1 -1
- package/dist/utils/conversation.d.ts +1 -0
- package/dist/utils/conversation.d.ts.map +1 -1
- package/dist/utils/conversation.js +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
- package/dist/screens/desk-message/ViewportNotice.d.ts +0 -4
- package/dist/screens/desk-message/ViewportNotice.d.ts.map +0 -1
- package/dist/screens/desk-message/ViewportNotice.js +0 -9
|
@@ -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":"ManualAssignPopover.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ManualAssignPopover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ManualAssignPopover.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ManualAssignPopover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAsBzC,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,UAAU,wBAAwB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;CACvC;AA0ED,QAAA,MAAM,mBAAmB,GAAI,wFAO1B,wBAAwB,4CA+P1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -13,12 +13,16 @@ import AssignConfirmModal from "./AssignConfirmModal";
|
|
|
13
13
|
import { useDebounce } from "ahooks";
|
|
14
14
|
import useAuthStore from "../../store/auth";
|
|
15
15
|
import TintedAvatar from "../../components/common/TintedAvatar";
|
|
16
|
+
import BottomSheet from "../../components/common/BottomSheet";
|
|
17
|
+
import { useViewportTier } from "../../hooks/common/useViewportTier";
|
|
18
|
+
import { useMediaQuery } from "../../hooks/common/useMediaQuery";
|
|
19
|
+
import { LAYOUT_MEDIA } from "../../constants/layout";
|
|
16
20
|
const SEARCH_DEBOUNCE_WAIT = 300;
|
|
17
21
|
const SupporterItem = ({ supporter, onClick, isCurrentUser, }) => {
|
|
18
22
|
var _a;
|
|
19
23
|
const { t } = useTranslation();
|
|
20
24
|
const isOnline = supporter.status === "ONLINE";
|
|
21
|
-
return (_jsxs("div", { role: "option", "aria-selected": false, tabIndex: 0, className: "flex items-center gap-3 px-3 py-2 rounded-lg transition-colors cursor-pointer hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", onClick: () => onClick(supporter), onKeyDown: (e) => {
|
|
25
|
+
return (_jsxs("div", { role: "option", "aria-selected": false, tabIndex: 0, className: "flex items-center gap-3 px-3 py-2 rounded-lg transition-colors cursor-pointer hover:bg-gray-5 touch:min-h-12 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", onClick: () => onClick(supporter), onKeyDown: (e) => {
|
|
22
26
|
if (e.key === "Enter" || e.key === " ") {
|
|
23
27
|
e.preventDefault();
|
|
24
28
|
onClick(supporter);
|
|
@@ -96,6 +100,8 @@ const ManualAssignPopover = ({ sessionId, supporter, teamId: propTeamId, childre
|
|
|
96
100
|
const handleAfterClose = () => {
|
|
97
101
|
setSelectedAgent(null);
|
|
98
102
|
};
|
|
103
|
+
const isPhone = useViewportTier() === "phone";
|
|
104
|
+
const isTouch = useMediaQuery(LAYOUT_MEDIA.touch);
|
|
99
105
|
const handleOpenChange = (newOpen) => {
|
|
100
106
|
if (isSubmitting)
|
|
101
107
|
return;
|
|
@@ -104,9 +110,9 @@ const ManualAssignPopover = ({ sessionId, supporter, teamId: propTeamId, childre
|
|
|
104
110
|
setSearchText("");
|
|
105
111
|
}
|
|
106
112
|
};
|
|
107
|
-
const popoverContent = (_jsxs("div", { className: "w-64", children: [_jsxs("button", { type: "button", className: clsx("flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm transition-colors", isAutoAssignDisabled
|
|
113
|
+
const popoverContent = (_jsxs("div", { className: isPhone ? "w-full" : "w-64", children: [_jsxs("button", { type: "button", className: clsx("flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm transition-colors", isAutoAssignDisabled
|
|
108
114
|
? "cursor-not-allowed text-gray-400"
|
|
109
|
-
: "text-gray-900 hover:bg-gray-5"), onClick: handleAutoAssign, disabled: isAutoAssignDisabled, children: [isAutoAssigning ? (_jsx(Spin, { size: "small" })) : (_jsx("span", { className: "text-primary-400", children: _jsx(AdaptiveIcon, { stroke: "refresh-cw", font: "refresh-b", size: 20, strokeSize: 18 }) })), _jsx("span", { children: t("manual_assign_button_auto_reassign") })] }), _jsx(Divider, { className: "my-2" }), _jsx("div", { className: "mb-2", children: _jsx(Input, { autoFocus:
|
|
115
|
+
: "text-gray-900 hover:bg-gray-5"), onClick: handleAutoAssign, disabled: isAutoAssignDisabled, children: [isAutoAssigning ? (_jsx(Spin, { size: "small" })) : (_jsx("span", { className: "text-primary-400", children: _jsx(AdaptiveIcon, { stroke: "refresh-cw", font: "refresh-b", size: 20, strokeSize: 18 }) })), _jsx("span", { children: t("manual_assign_button_auto_reassign") })] }), _jsx(Divider, { className: "my-2" }), _jsx("div", { className: "mb-2", children: _jsx(Input, { autoFocus: !isTouch, placeholder: t("manual_assign_search_placeholder"), value: searchText, onChange: (e) => setSearchText(e.target.value), prefix: _jsx(AdaptiveIcon, { stroke: "search", font: "search-o", size: 16, className: "text-gray-400" }), className: "rounded-lg border-gray-300 focus-within:border-chat-focus focus-within:shadow-chat-focus-ring", allowClear: true }) }), _jsx("div", { className: "max-h-72 overflow-y-auto", role: "listbox", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Spin, {}) })) : supporters && supporters.length > 0 ? (_jsx("div", { className: clsx("flex flex-col transition-opacity duration-200", isFetching && "opacity-50 pointer-events-none"), children: [...supporters]
|
|
110
116
|
.filter((item) => item.memberId !== (supporter === null || supporter === void 0 ? void 0 : supporter.id))
|
|
111
117
|
.filter((item) => !excludeCurrentUser || item.memberId !== authUserID)
|
|
112
118
|
.sort((supporterA, supporterB) => {
|
|
@@ -120,8 +126,9 @@ const ManualAssignPopover = ({ sessionId, supporter, teamId: propTeamId, childre
|
|
|
120
126
|
});
|
|
121
127
|
})
|
|
122
128
|
.map((item) => (_jsx(SupporterItem, { supporter: item, onClick: handleSelectAgent, isCurrentUser: item.memberId === authUserID }, item.memberId))) })) : (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx("span", { className: "text-sm text-gray-400", children: t("manual_assign_no_results") }) })) })] }));
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
const triggerNode = children !== null && children !== void 0 ? children : (_jsxs("button", { type: "button", "aria-label": t("manual_assign_button_reassign"), className: clsx("flex items-center border border-gray-200 rounded-lg p-1 gap-1 bg-transparent transition-opacity focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", isSubmitting
|
|
130
|
+
? "opacity-50 pointer-events-none"
|
|
131
|
+
: "cursor-pointer hover:opacity-80"), children: [_jsx(TintedAvatar, { name: (_a = supporter === null || supporter === void 0 ? void 0 : supporter.fullName) !== null && _a !== void 0 ? _a : "", src: supporter === null || supporter === void 0 ? void 0 : supporter.avatar, size: 28 }), _jsx("span", { className: "text-primary-400", children: _jsx(AdaptiveIcon, { stroke: "shuffle", font: "shuffle-b", size: 20, strokeSize: 18 }) })] }));
|
|
132
|
+
return (_jsxs(_Fragment, { children: [isPhone ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "contents", onClick: () => handleOpenChange(true), children: triggerNode }), _jsx(BottomSheet, { open: open, title: t("manual_assign_button_reassign"), onClose: () => handleOpenChange(false), children: popoverContent })] })) : (_jsx(Popover, { content: popoverContent, trigger: "click", open: open, onOpenChange: handleOpenChange, placement: placement, arrow: false, children: triggerNode })), _jsx(AssignConfirmModal, { open: confirmModalOpen, agent: selectedAgent, loading: isAssigning, onCancel: handleCancelConfirm, onConfirm: handleConfirmAssign, afterClose: handleAfterClose })] }));
|
|
126
133
|
};
|
|
127
134
|
export default ManualAssignPopover;
|
|
@@ -99,5 +99,5 @@ export const SessionDetailCard = ({ session, isActive, }) => {
|
|
|
99
99
|
time: formatSessionTime(session.createdDate),
|
|
100
100
|
})
|
|
101
101
|
: formatSessionDate(session.closedDate || session.createdDate) }), _jsx("span", { className: "flex-1" }), isActive ? (_jsx("span", { className: "inline-flex h-[22px] items-center rounded-full bg-success-25 px-2 text-xs font-medium text-success-900", children: t("session_active") })) : (_jsx(TintedAvatar, { name: ((_a = session.supporter) === null || _a === void 0 ? void 0 : _a.fullName) || "A", src: (_b = session.supporter) === null || _b === void 0 ? void 0 : _b.avatar, size: 24 }))] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { htmlFor: `${ids}-tags`, className: fieldLabelClass, children: t("session_tags_label") }), _jsx(SessionTagPicker, { labelId: `${ids}-tags`, available: availableLabels !== null && availableLabels !== void 0 ? availableLabels : [], selectedIds: (_d = (_c = session.labels) === null || _c === void 0 ? void 0 : _c.map((label) => label.id)) !== null && _d !== void 0 ? _d : [], loading: isLoadingLabels, onChange: handleUpdateLabels })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("label", { htmlFor: `${ids}-issue`, className: fieldLabelClass, children: t("session_issue_detail_label") }), _jsx(TextArea, { id: `${ids}-issue`, value: issueDetail.value, onChange: (e) => issueDetail.setValue(e.target.value), placeholder: t("session_issue_detail_placeholder"), className: `${fieldClass} !min-h-9`, autoSize: { minRows: 1, maxRows: 3 } })] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: `${ids}-note`, className: fieldLabelClass, children: t("session_note_label") }), _jsx(SaveStatus, { status: saveStatus, onRetry: retrySave })] }), _jsx(TextArea, { id: `${ids}-note`, value: note.value, onChange: (e) => note.setValue(e.target.value), placeholder: t("session_note_placeholder"), className: fieldClass, autoSize: { minRows: 2, maxRows: 4 } })] }), isActive && (_jsxs(_Fragment, { children: [session.status !== SessionStatus.UNASSIGNED &&
|
|
102
|
-
((_e = session.supporter) === null || _e === void 0 ? void 0 : _e.id) && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TintedAvatar, { name: session.supporter.fullName || "A", src: session.supporter.avatar, size: 28 }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "text-xs text-gray-600", children: t("session_owner_label") }), _jsx("span", { className: "truncate text-[13px] font-medium text-gray-900", children: session.supporter.fullName })] }), _jsx(ManualAssignPopover, { sessionId: session.id, supporter: session.supporter, teamId: session.teamId, placement: "topRight", children: _jsx(Button, { icon: _jsx(StrokeIcon, { name: "shuffle", size: 18 }), className: "!h-8 !rounded-lg !px-3.5 !text-[13px] !font-medium", children: t("manual_assign_button_reassign") }) })] })), _jsx(Button, { block: true, icon: _jsx(StrokeIcon, { name: "check-circle", size: 18 }), onClick: () => setIsEndConfirmOpen(true), disabled: isEnding, children: isEnding ? t("end_session_ending") : t("session_close_button") }), _jsx(EndSessionConfirm, { open: isEndConfirmOpen, session: session, customerName: customerName, onConfirm: handleConfirmEnd, onCancel: () => setIsEndConfirmOpen(false) })] }))] }));
|
|
102
|
+
((_e = session.supporter) === null || _e === void 0 ? void 0 : _e.id) && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TintedAvatar, { name: session.supporter.fullName || "A", src: session.supporter.avatar, size: 28 }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "text-xs text-gray-600", children: t("session_owner_label") }), _jsx("span", { className: "truncate text-[13px] font-medium text-gray-900", children: session.supporter.fullName })] }), _jsx(ManualAssignPopover, { sessionId: session.id, supporter: session.supporter, teamId: session.teamId, placement: "topRight", children: _jsx(Button, { icon: _jsx(StrokeIcon, { name: "shuffle", size: 18 }), className: "!h-8 touch:!h-10 !rounded-lg !px-3.5 !text-[13px] !font-medium", children: t("manual_assign_button_reassign") }) })] })), _jsx(Button, { block: true, icon: _jsx(StrokeIcon, { name: "check-circle", size: 18 }), onClick: () => setIsEndConfirmOpen(true), disabled: isEnding, children: isEnding ? t("end_session_ending") : t("session_close_button") }), _jsx(EndSessionConfirm, { open: isEndConfirmOpen, session: session, customerName: customerName, onConfirm: handleConfirmEnd, onCancel: () => setIsEndConfirmOpen(false) })] }))] }));
|
|
103
103
|
};
|
|
@@ -19,7 +19,7 @@ const SessionTagPicker = ({ labelId, available, selectedIds, loading, onChange,
|
|
|
19
19
|
event.stopPropagation();
|
|
20
20
|
setOpen(false);
|
|
21
21
|
}
|
|
22
|
-
}, children: [_jsxs("div", { className: "flex min-h-9 flex-wrap items-center gap-1 rounded-lg border border-gray-50 px-1.5 py-1", children: [selected.map((label) => (_jsxs("span", { className: chipClass, children: [label.name, _jsx("button", { type: "button", "aria-label": t("session_remove_tag", { name: label.name }), className: "flex h-4 w-4 items-center justify-center rounded-full text-gray-600 hover:bg-gray-50", onClick: () => toggle(label.id), children: _jsx(StrokeIcon, { name: "x", size: 12 }) })] }, label.id))), _jsxs("button", { id: labelId, type: "button", "aria-label": t("session_add_tag"), "aria-expanded": open, "aria-controls": listId, className: "flex h-6 items-center gap-1 rounded px-1 text-[13px] text-gray-600 hover:text-gray-900", onClick: () => setOpen((value) => !value), children: [_jsx(StrokeIcon, { name: "plus", size: 14 }), t("session_add_tag")] })] }), open && (_jsxs("ul", { id: listId, role: "listbox", "aria-multiselectable": true, "aria-labelledby": labelId, className: "mt-1 max-h-40 overflow-y-auto rounded-lg border border-gray-50 p-1", children: [loading && (_jsx("li", { className: "flex justify-center py-2", children: _jsx(Spin, { size: "small" }) })), !loading && available.length === 0 && (_jsx("li", { className: "px-2 py-1.5 text-[13px] text-gray-600", children: t("session_no_tags") })), available.map((label) => {
|
|
22
|
+
}, children: [_jsxs("div", { className: "flex min-h-9 flex-wrap items-center gap-1 rounded-lg border border-gray-50 px-1.5 py-1", children: [selected.map((label) => (_jsxs("span", { className: chipClass, children: [label.name, _jsx("button", { type: "button", "aria-label": t("session_remove_tag", { name: label.name }), className: "flex h-4 w-4 items-center justify-center rounded-full text-gray-600 hover:bg-gray-50", onClick: () => toggle(label.id), children: _jsx(StrokeIcon, { name: "x", size: 12 }) })] }, label.id))), _jsxs("button", { id: labelId, type: "button", "aria-label": t("session_add_tag"), "aria-expanded": open, "aria-controls": listId, className: "flex h-6 touch:-my-2 touch:min-h-10 items-center gap-1 rounded px-1 text-[13px] text-gray-600 hover:text-gray-900", onClick: () => setOpen((value) => !value), children: [_jsx(StrokeIcon, { name: "plus", size: 14 }), t("session_add_tag")] })] }), open && (_jsxs("ul", { id: listId, role: "listbox", "aria-multiselectable": true, "aria-labelledby": labelId, className: "mt-1 max-h-40 overflow-y-auto rounded-lg border border-gray-50 p-1", children: [loading && (_jsx("li", { className: "flex justify-center py-2", children: _jsx(Spin, { size: "small" }) })), !loading && available.length === 0 && (_jsx("li", { className: "px-2 py-1.5 text-[13px] text-gray-600", children: t("session_no_tags") })), available.map((label) => {
|
|
23
23
|
const isSelected = selectedIds.includes(label.id);
|
|
24
24
|
return (_jsx("li", { role: "option", "aria-selected": isSelected, children: _jsxs("button", { type: "button", className: `flex h-8 w-full items-center justify-between rounded px-2 text-left text-[13px] text-gray-900 hover:bg-gray-5 ${isSelected ? "bg-primary-25" : ""}`, onClick: () => toggle(label.id), children: [label.name, isSelected && _jsx(StrokeIcon, { name: "check", size: 14 })] }) }, label.id));
|
|
25
25
|
})] }))] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupportSessionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SupportSessionPanel.tsx"],"names":[],"mappings":"AAeA,UAAU,wBAAwB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"SupportSessionPanel.d.ts","sourceRoot":"","sources":["../../../src/components/thread/SupportSessionPanel.tsx"],"names":[],"mappings":"AAeA,UAAU,wBAAwB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,sFAAsF;IACtF,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;GAGG;AACH,QAAA,MAAM,mBAAmB,GAAI,4FAQ1B,wBAAwB,4CAgG1B,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -41,6 +41,6 @@ const SupportSessionPanel = ({ conversationId, width, isDetailsOpen = false, onC
|
|
|
41
41
|
(_a = detailsButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
42
42
|
wasDetailsOpen.current = isDetailsOpen;
|
|
43
43
|
}, [isDetailsOpen]);
|
|
44
|
-
return (_jsxs("div", { ref: setPanelEl, className: "relative flex h-full shrink-0 flex-col overflow-hidden rounded-xl bg-white shadow-sm", style: { width }, "data-cp-name": "SupportSessionPanel", "data-testid": "session-info-column", children: [variant === "drawer" ? (_jsx(ThreadInfoHeader, { title: t("support_session_panel_title"), onClose: onClose, actions: _jsx("button", { ref: detailsButtonRef, type: "button", "aria-label": t("thread_info_open"), title: t("thread_info_open"), "aria-expanded": isDetailsOpen, onClick: onOpenDetails, className: threadInfoIconButtonClass, children: _jsx(StrokeIcon, { name: "info", size: 18 }) }) })) : (_jsx("div", { className: "flex h-14 shrink-0 items-center border-b border-gray-25 px-4", children: _jsx("h2", { className: "m-0 truncate text-base font-bold text-gray-900", children: t("support_session_panel_title") }) })), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [_jsx(UserSection, {}), _jsx(SessionSection, { sessions: closedSessions, isLoading: isLoading })] }), activeSession && (_jsx("div", { className: "max-h-[70%] shrink-0 overflow-y-auto border-t border-gray-25 bg-white p-4", "data-testid": "active-session-dock", children: _jsx(SessionDetailCard, { session: activeSession, isActive: true }, activeSession.id) })), variant === "drawer" && panelEl && (_jsx(Drawer, { open: isDetailsOpen, onClose: onCloseDetails, "aria-label": t("thread_info_title"), getContainer: panelEl, rootStyle: { position: "absolute" }, width: "100%", mask: false, closeIcon: false, destroyOnHidden: true, styles: { body: { padding: 0 }, wrapper: { boxShadow: "none" } }, children: _jsx("div", { className: "h-full", "data-testid": "thread-details-overlay", children: _jsx(SingleThreadInfo, { onClose: onCloseDetails }) }) }))] }));
|
|
44
|
+
return (_jsxs("div", { ref: setPanelEl, className: "relative flex h-full shrink-0 flex-col overflow-hidden rounded-xl bg-white shadow-sm phone:rounded-none phone:shadow-none", style: { width }, "data-cp-name": "SupportSessionPanel", "data-testid": "session-info-column", children: [variant === "drawer" ? (_jsx(ThreadInfoHeader, { title: t("support_session_panel_title"), onClose: onClose, actions: _jsx("button", { ref: detailsButtonRef, type: "button", "aria-label": t("thread_info_open"), title: t("thread_info_open"), "aria-expanded": isDetailsOpen, onClick: onOpenDetails, className: threadInfoIconButtonClass, children: _jsx(StrokeIcon, { name: "info", size: 18 }) }) })) : (_jsx("div", { className: "flex h-14 shrink-0 items-center border-b border-gray-25 px-4", children: _jsx("h2", { className: "m-0 truncate text-base font-bold text-gray-900", children: t("support_session_panel_title") }) })), _jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [_jsx(UserSection, {}), _jsx(SessionSection, { sessions: closedSessions, isLoading: isLoading })] }), activeSession && (_jsx("div", { className: "max-h-[70%] shrink-0 overflow-y-auto border-t border-gray-25 bg-white p-4", "data-testid": "active-session-dock", children: _jsx(SessionDetailCard, { session: activeSession, isActive: true }, activeSession.id) })), variant === "drawer" && panelEl && (_jsx(Drawer, { open: isDetailsOpen, onClose: onCloseDetails, "aria-label": t("thread_info_title"), getContainer: panelEl, rootStyle: { position: "absolute" }, width: "100%", mask: false, closeIcon: false, destroyOnHidden: true, styles: { body: { padding: 0 }, wrapper: { boxShadow: "none" } }, children: _jsx("div", { className: "h-full", "data-testid": "thread-details-overlay", children: _jsx(SingleThreadInfo, { onClose: onCloseDetails }) }) }))] }));
|
|
45
45
|
};
|
|
46
46
|
export default SupportSessionPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfo.tsx"],"names":[],"mappings":"AAGA,OAAwB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfo.tsx"],"names":[],"mappings":"AAGA,OAAwB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAW3E,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,iGAAiG;IACjG,eAAe,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,QAAA,MAAM,UAAU,GAAI,wEAMjB,eAAe,mDAwEjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
|
|
@@ -11,12 +11,14 @@ import { isGroupConversation } from "../../utils/imCommon";
|
|
|
11
11
|
import { LAYOUT_WIDTHS } from "../../constants/layout";
|
|
12
12
|
import DeskSidePanel from "../../components/common/DeskSidePanel";
|
|
13
13
|
import DeskPanelDrawer from "../../components/common/DeskPanelDrawer";
|
|
14
|
+
import useDeskPanelHostStore from "../../store/deskPanelHost";
|
|
14
15
|
const ThreadInfo = ({ isOpen = true, onClose, groupConfig, drawerContainer, supportPanelInline = true, }) => {
|
|
15
16
|
const { t } = useTranslation();
|
|
16
17
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
17
18
|
const conversationId = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationID) || "";
|
|
18
19
|
const isGroup = isGroupConversation(conversationData !== null && conversationData !== void 0 ? conversationData : undefined);
|
|
19
20
|
const isBot = (conversationData === null || conversationData === void 0 ? void 0 : conversationData.peerType) === PeerType.BOT;
|
|
21
|
+
const isFullScreen = useDeskPanelHostStore((state) => state.isFullScreen);
|
|
20
22
|
// Narrow screens only: "Thông tin" over the "Phiên hỗ trợ" drawer, opened from inside it.
|
|
21
23
|
const [isNarrowDetailsOpen, setIsNarrowDetailsOpen] = useState(false);
|
|
22
24
|
useEffect(() => {
|
|
@@ -37,6 +39,6 @@ const ThreadInfo = ({ isOpen = true, onClose, groupConfig, drawerContainer, supp
|
|
|
37
39
|
}
|
|
38
40
|
return (_jsx(DeskPanelDrawer, { open: isOpen, label: t("support_session_panel_title"), onClose: () => onClose === null || onClose === void 0 ? void 0 : onClose(), container: drawerContainer,
|
|
39
41
|
// Esc closes "Thông tin" first, then the drawer.
|
|
40
|
-
keyboard: !isNarrowDetailsOpen, children: _jsx(SupportSessionPanel, { conversationId: conversationId, width: LAYOUT_WIDTHS.infoDrawer, isDetailsOpen: isNarrowDetailsOpen, onCloseDetails: () => setIsNarrowDetailsOpen(false), variant: "drawer", onClose: onClose, onOpenDetails: () => setIsNarrowDetailsOpen(true) }) }));
|
|
42
|
+
keyboard: !isNarrowDetailsOpen, children: _jsx(SupportSessionPanel, { conversationId: conversationId, width: isFullScreen ? "100%" : LAYOUT_WIDTHS.infoDrawer, isDetailsOpen: isNarrowDetailsOpen, onCloseDetails: () => setIsNarrowDetailsOpen(false), variant: "drawer", onClose: onClose, onOpenDetails: () => setIsNarrowDetailsOpen(true) }) }));
|
|
41
43
|
};
|
|
42
44
|
export default ThreadInfo;
|
|
@@ -6,7 +6,7 @@ interface ThreadInfoHeaderProps {
|
|
|
6
6
|
/** Extra icon buttons placed before the close button. */
|
|
7
7
|
actions?: ReactNode;
|
|
8
8
|
}
|
|
9
|
-
export declare const threadInfoIconButtonClass = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
|
|
9
|
+
export declare const threadInfoIconButtonClass = "flex h-8 w-8 touch:min-h-10 touch:min-w-10 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
|
|
10
10
|
/** 56px title bar shared by the right-hand panels, inline and in the drawer. */
|
|
11
11
|
declare const ThreadInfoHeader: ({ title, onClose, actions }: ThreadInfoHeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
12
|
export default ThreadInfoHeader;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThreadInfoHeader.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfoHeader.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlC,UAAU,qBAAqB;IAC7B,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,yDAAyD;IACzD,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"ThreadInfoHeader.d.ts","sourceRoot":"","sources":["../../../src/components/thread/ThreadInfoHeader.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlC,UAAU,qBAAqB;IAC7B,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,yDAAyD;IACzD,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,yBAAyB,6MACsK,CAAC;AAE7M,gFAAgF;AAChF,QAAA,MAAM,gBAAgB,GAAI,6BAA6B,qBAAqB,4CAmB3E,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { StrokeIcon } from "../../components/icon/StrokeIcon";
|
|
5
|
-
export const threadInfoIconButtonClass = "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
|
|
5
|
+
export const threadInfoIconButtonClass = "flex h-8 w-8 touch:min-h-10 touch:min-w-10 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200";
|
|
6
6
|
/** 56px title bar shared by the right-hand panels, inline and in the drawer. */
|
|
7
7
|
const ThreadInfoHeader = ({ title, onClose, actions }) => {
|
|
8
8
|
const { t } = useTranslation();
|
|
@@ -83,12 +83,12 @@ const UserSection = () => {
|
|
|
83
83
|
message.success(t("copied_to_clipboard"));
|
|
84
84
|
};
|
|
85
85
|
const rowLabelClass = "text-gray-600";
|
|
86
|
-
const copyButtonClass = "flex h-6 w-6 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5";
|
|
86
|
+
const copyButtonClass = "flex h-6 w-6 shrink-0 items-center justify-center rounded-lg text-gray-600 hover:bg-gray-5 touch:-m-2 touch:min-h-10 touch:min-w-10";
|
|
87
87
|
const fullName = (data === null || data === void 0 ? void 0 : data.fullName) || "";
|
|
88
88
|
return (_jsxs("div", { className: "flex flex-col gap-4 px-4 py-4", "data-cp-name": "UserSection", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(TintedAvatar, { name: fullName, src: data === null || data === void 0 ? void 0 : data.avatar, size: 48 }), _jsx("div", { className: "flex min-w-0 flex-col gap-1", children: _jsx("p", { className: "m-0 truncate text-base font-semibold text-gray-900", children: fullName }) })] }), _jsxs("dl", { className: "m-0 grid grid-cols-[112px_minmax(0,1fr)] items-center gap-y-2 text-[13px]", children: [_jsx("dt", { className: rowLabelClass, children: t("user_username_label") }), _jsxs("dd", { className: "m-0 flex min-w-0 items-center gap-1 text-gray-900", children: [_jsx("span", { className: "truncate", children: (data === null || data === void 0 ? void 0 : data.username) || "" }), _jsx("button", { type: "button", className: copyButtonClass, "aria-label": t("user_copy_username"), title: t("user_copy_username"), onClick: () => copyValue((data === null || data === void 0 ? void 0 : data.username) || ""), children: _jsx(StrokeIcon, { name: "copy", size: 14 }) })] }), _jsx("dt", { className: rowLabelClass, children: t("user_id_label") }), _jsxs("dd", { className: "m-0 flex min-w-0 items-center gap-1 text-gray-900", children: [_jsx("span", { className: "truncate", title: (data === null || data === void 0 ? void 0 : data.id) || undefined, children: middleEllipsis((data === null || data === void 0 ? void 0 : data.id) || "") }), _jsx("button", { type: "button", className: copyButtonClass, "aria-label": t("user_copy_id"), title: t("user_copy_id"), onClick: () => copyValue((data === null || data === void 0 ? void 0 : data.id) || ""), children: _jsx(StrokeIcon, { name: "copy", size: 14 }) })] }), _jsx("dt", { className: `${rowLabelClass} self-start`, children: _jsx("label", { htmlFor: "customer-note", children: t("user_note_label") }) }), _jsx("dd", { className: "m-0", children: isEditingNote ? (_jsx("textarea", { id: "customer-note", autoFocus: true, rows: Math.min(Math.max(noteValue.split("\n").length, 2), 6), value: noteValue, placeholder: t("add_note"), className: "w-full resize-none rounded-lg border border-gray-50 px-2.5 py-1.5 text-[13px] text-gray-900 outline-none focus:border-primary-400", onChange: (e) => {
|
|
89
89
|
const v = e.target.value;
|
|
90
90
|
setNoteValue(v);
|
|
91
91
|
scheduleSave(v);
|
|
92
|
-
}, onBlur: () => setIsEditingNote(false) })) : (_jsx("button", { id: "customer-note", type: "button", onClick: () => setIsEditingNote(true), className: "m-0 w-full whitespace-pre-wrap break-words border-0 bg-transparent p-0 text-left text-[13px] font-medium leading-5 text-primary-500 hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: noteValue ? (_jsx("span", { className: "font-normal text-gray-900", children: noteValue })) : (t("add_note")) })) })] })] }));
|
|
92
|
+
}, onBlur: () => setIsEditingNote(false) })) : (_jsx("button", { id: "customer-note", type: "button", onClick: () => setIsEditingNote(true), className: "m-0 w-full whitespace-pre-wrap break-words border-0 bg-transparent p-0 touch:-my-2.5 touch:min-h-10 text-left text-[13px] font-medium leading-5 text-primary-500 hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-200", children: noteValue ? (_jsx("span", { className: "font-normal text-gray-900", children: noteValue })) : (t("add_note")) })) })] })] }));
|
|
93
93
|
};
|
|
94
94
|
export default UserSection;
|
|
@@ -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,6 +1,8 @@
|
|
|
1
1
|
/** Viewport breakpoints (px) for the CRM desk workspace. */
|
|
2
2
|
export declare const LAYOUT_BREAKPOINTS: {
|
|
3
|
-
/**
|
|
3
|
+
/** Phone/tablet boundary (see LAYOUT_MEDIA for the full phone rule). */
|
|
4
|
+
readonly tablet: 768;
|
|
5
|
+
/** From this width the desktop desk layout applies; below it the desk uses the tablet or phone layout. */
|
|
4
6
|
readonly minWorkspace: 1024;
|
|
5
7
|
/** From this width the folder panel stays inline; below it, it overlays the list. */
|
|
6
8
|
readonly folderInline: 1600;
|
|
@@ -21,4 +23,11 @@ export declare const LAYOUT_WIDTHS: {
|
|
|
21
23
|
readonly infoDrawer: 360;
|
|
22
24
|
readonly chatMin: 480;
|
|
23
25
|
};
|
|
26
|
+
/** Media queries behind the viewport tiers; keep in sync with the phone/touch variants in apps/web/tailwind.config.js. */
|
|
27
|
+
export declare const LAYOUT_MEDIA: {
|
|
28
|
+
readonly phone: `not all and (min-width: 768px), (pointer: coarse) and (max-height: 499px) and (max-width: ${number}px)`;
|
|
29
|
+
readonly belowDesktop: "not all and (min-width: 1024px)";
|
|
30
|
+
readonly touch: `(hover: none) and (pointer: coarse) and (max-width: ${number}px)`;
|
|
31
|
+
readonly touchAnyWidth: "(hover: none) and (pointer: coarse)";
|
|
32
|
+
};
|
|
24
33
|
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/constants/layout.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB;IAC7B
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/constants/layout.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB;IAC7B,wEAAwE;;IAExE,0GAA0G;;IAE1G,qFAAqF;;IAErF,mGAAmG;;IAEnG,qFAAqF;;IAErF,kGAAkG;;CAE1F,CAAC;AAEX,+CAA+C;AAC/C,eAAO,MAAM,aAAa;;;;;;;;CAQhB,CAAC;AAEX,0HAA0H;AAC1H,eAAO,MAAM,YAAY;;;;;CAKf,CAAC"}
|
package/dist/constants/layout.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Viewport breakpoints (px) for the CRM desk workspace. */
|
|
2
2
|
export const LAYOUT_BREAKPOINTS = {
|
|
3
|
-
/**
|
|
3
|
+
/** Phone/tablet boundary (see LAYOUT_MEDIA for the full phone rule). */
|
|
4
|
+
tablet: 768,
|
|
5
|
+
/** From this width the desktop desk layout applies; below it the desk uses the tablet or phone layout. */
|
|
4
6
|
minWorkspace: 1024,
|
|
5
7
|
/** From this width the folder panel stays inline; below it, it overlays the list. */
|
|
6
8
|
folderInline: 1600,
|
|
@@ -21,3 +23,10 @@ export const LAYOUT_WIDTHS = {
|
|
|
21
23
|
infoDrawer: 360,
|
|
22
24
|
chatMin: 480,
|
|
23
25
|
};
|
|
26
|
+
/** Media queries behind the viewport tiers; keep in sync with the phone/touch variants in apps/web/tailwind.config.js. */
|
|
27
|
+
export const LAYOUT_MEDIA = {
|
|
28
|
+
phone: `not all and (min-width: ${LAYOUT_BREAKPOINTS.tablet}px), (pointer: coarse) and (max-height: 499px) and (max-width: ${LAYOUT_BREAKPOINTS.minWorkspace - 1}px)`,
|
|
29
|
+
belowDesktop: `not all and (min-width: ${LAYOUT_BREAKPOINTS.minWorkspace}px)`,
|
|
30
|
+
touch: `(hover: none) and (pointer: coarse) and (max-width: ${LAYOUT_BREAKPOINTS.minWorkspace - 1}px)`,
|
|
31
|
+
touchAnyWidth: "(hover: none) and (pointer: coarse)",
|
|
32
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeskShortcuts.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useDeskShortcuts.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,iBAAiB,2CAAyC,CAAC;AACxE,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AACzD,eAAO,MAAM,sBAAsB,sCAAoC,CAAC;AAExE,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,GAAG,IAAI,YAQxD,CAAC;
|
|
1
|
+
{"version":3,"file":"useDeskShortcuts.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useDeskShortcuts.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,iBAAiB,2CAAyC,CAAC;AACxE,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AACzD,eAAO,MAAM,sBAAsB,sCAAoC,CAAC;AAExE,eAAO,MAAM,cAAc,GAAI,QAAQ,WAAW,GAAG,IAAI,YAQxD,CAAC;AA6CF,UAAU,uBAAuB;IAC/B,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,GAAI,gBAAgB,uBAAuB,SAsCvE,CAAC"}
|
|
@@ -15,7 +15,9 @@ export const isTypingTarget = (target) => {
|
|
|
15
15
|
["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName) ||
|
|
16
16
|
target.getAttribute("role") === "textbox");
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
// Overlays that mark themselves with the attribute (nav drawer, bottom sheets, offline lock) pause shortcuts.
|
|
19
|
+
const isModalOpen = () => document.querySelector("[data-desk-shortcuts-off]") !== null ||
|
|
20
|
+
Array.from(document.querySelectorAll(".ant-modal-wrap")).some((el) => el.style.display !== "none");
|
|
19
21
|
/** Moves to the next/previous row in the order the list is rendered (filtered and loaded). */
|
|
20
22
|
const moveListSelection = (step) => {
|
|
21
23
|
var _a, _b;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsDeskWorkspaceMounted.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useIsDeskWorkspaceMounted.ts"],"names":[],"mappings":"AAGA,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,QAAO,OACe,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import useDeskPanelHostStore from "../../store/deskPanelHost";
|
|
3
|
+
/** True while the CRM desk workspace is on screen; hosts use it to switch their own chrome. */
|
|
4
|
+
export const useIsDeskWorkspaceMounted = () => useDeskPanelHostStore((state) => state.workspace !== null);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useViewportTier.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useViewportTier.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1D,gGAAgG;AAChG,eAAO,MAAM,eAAe,QAAO,YAIlC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { LAYOUT_MEDIA } from "../../constants/layout";
|
|
3
|
+
import { useMediaQuery } from "./useMediaQuery";
|
|
4
|
+
/** Desk/shell tier; unmatched or unavailable queries read as desktop so ≥1024 never changes. */
|
|
5
|
+
export const useViewportTier = () => {
|
|
6
|
+
const isPhone = useMediaQuery(LAYOUT_MEDIA.phone);
|
|
7
|
+
const isBelowDesktop = useMediaQuery(LAYOUT_MEDIA.belowDesktop);
|
|
8
|
+
return isPhone ? "phone" : isBelowDesktop ? "tablet" : "desktop";
|
|
9
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Visible (keyboard-aware) viewport height, set on the desk root. */
|
|
2
|
+
export declare const DESK_HEIGHT_VAR = "--dc-desk-height";
|
|
3
|
+
/** Same height on <html>, for portaled bottom sheets. */
|
|
4
|
+
export declare const VISUAL_HEIGHT_VAR = "--dc-visual-height";
|
|
5
|
+
/**
|
|
6
|
+
* Mirrors `visualViewport.height` into the height vars while `enabled` (resize only).
|
|
7
|
+
* WebKit ignores `interactive-widget`; zoom is cancelled out with `scale`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const useVisualViewportHeight: (el: HTMLElement | null, enabled: boolean) => void;
|
|
10
|
+
//# sourceMappingURL=useVisualViewportHeight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useVisualViewportHeight.d.ts","sourceRoot":"","sources":["../../../src/hooks/common/useVisualViewportHeight.ts"],"names":[],"mappings":"AAGA,sEAAsE;AACtE,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,IAAI,WAAW,GAAG,IAAI,EACtB,SAAS,OAAO,SAoBjB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
/** Visible (keyboard-aware) viewport height, set on the desk root. */
|
|
4
|
+
export const DESK_HEIGHT_VAR = "--dc-desk-height";
|
|
5
|
+
/** Same height on <html>, for portaled bottom sheets. */
|
|
6
|
+
export const VISUAL_HEIGHT_VAR = "--dc-visual-height";
|
|
7
|
+
/**
|
|
8
|
+
* Mirrors `visualViewport.height` into the height vars while `enabled` (resize only).
|
|
9
|
+
* WebKit ignores `interactive-widget`; zoom is cancelled out with `scale`.
|
|
10
|
+
*/
|
|
11
|
+
export const useVisualViewportHeight = (el, enabled) => {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const vv = typeof window === "undefined" ? undefined : window.visualViewport;
|
|
14
|
+
if (!el || !enabled || !vv)
|
|
15
|
+
return;
|
|
16
|
+
const root = document.documentElement;
|
|
17
|
+
const sync = () => {
|
|
18
|
+
const value = `${Math.round(vv.height * (vv.scale || 1))}px`;
|
|
19
|
+
el.style.setProperty(DESK_HEIGHT_VAR, value);
|
|
20
|
+
root.style.setProperty(VISUAL_HEIGHT_VAR, value);
|
|
21
|
+
};
|
|
22
|
+
sync();
|
|
23
|
+
vv.addEventListener("resize", sync);
|
|
24
|
+
return () => {
|
|
25
|
+
vv.removeEventListener("resize", sync);
|
|
26
|
+
el.style.removeProperty(DESK_HEIGHT_VAR);
|
|
27
|
+
root.style.removeProperty(VISUAL_HEIGHT_VAR);
|
|
28
|
+
};
|
|
29
|
+
}, [el, enabled]);
|
|
30
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useConversationList.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useConversationList.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"useConversationList.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useConversationList.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAK/E,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,UAAU,yBAAyB;IACjC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AA4BD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,yBAAyB,KAChC,yBA4GF,CAAC"}
|
|
@@ -6,7 +6,25 @@ import { useChatContext } from "../../context/ChatContext";
|
|
|
6
6
|
import { ConnectStatus, DChatApplicationType } from "../../types/chat";
|
|
7
7
|
import useAuthStore from "../../store/auth";
|
|
8
8
|
import useConversationStore from "../../store/conversation";
|
|
9
|
+
import useBootStore from "../../store/boot";
|
|
9
10
|
const toConversationListItem = (conversation) => (Object.assign(Object.assign({}, conversation), { peerType: conversation.peerType }));
|
|
11
|
+
/** After an initial load: marks an empty list as really empty, or asks for one more load. */
|
|
12
|
+
const settleAfterLoad = (startedAfterSync, bootUserID, reload) => {
|
|
13
|
+
const boot = useBootStore.getState();
|
|
14
|
+
if (boot.userID !== bootUserID)
|
|
15
|
+
return;
|
|
16
|
+
if (!startedAfterSync) {
|
|
17
|
+
// Started before the first sync finished, so it may be empty on a fresh device: load again.
|
|
18
|
+
if (boot.isFirstSyncDone)
|
|
19
|
+
reload();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// A sync we only stopped waiting on can still be filling an empty local DB; its finish reloads.
|
|
23
|
+
const isEmpty = useConversationStore.getState().conversationList.length === 0;
|
|
24
|
+
if (boot.isSyncStalled && isEmpty)
|
|
25
|
+
return;
|
|
26
|
+
boot.markListSettled();
|
|
27
|
+
};
|
|
10
28
|
export const useConversationList = (params) => {
|
|
11
29
|
const { enabled, chatCategory } = params;
|
|
12
30
|
const { connectStatus } = useChatContext();
|
|
@@ -30,6 +48,7 @@ export const useConversationList = (params) => {
|
|
|
30
48
|
if (isLoadingInitialRef.current)
|
|
31
49
|
return;
|
|
32
50
|
isLoadingInitialRef.current = true;
|
|
51
|
+
const { isFirstSyncDone: startedAfterSync, userID: bootUserID } = useBootStore.getState();
|
|
33
52
|
setIsLoading(true);
|
|
34
53
|
try {
|
|
35
54
|
const hasMore = await getConversationListByReq(false, appParams);
|
|
@@ -38,6 +57,9 @@ export const useConversationList = (params) => {
|
|
|
38
57
|
finally {
|
|
39
58
|
isLoadingInitialRef.current = false;
|
|
40
59
|
setIsLoading(false);
|
|
60
|
+
settleAfterLoad(startedAfterSync, bootUserID, () => {
|
|
61
|
+
void loadInitialRef.current();
|
|
62
|
+
});
|
|
41
63
|
}
|
|
42
64
|
}, [getConversationListByReq, appParams]);
|
|
43
65
|
// Always points at the latest loadInitial (fresh appParams) without being
|
|
@@ -70,6 +92,15 @@ export const useConversationList = (params) => {
|
|
|
70
92
|
DChatSDK.off(CbEvents.OnSyncServerFinish, onSyncFinish);
|
|
71
93
|
};
|
|
72
94
|
}, [queryEnabled]);
|
|
95
|
+
// Also covers a sync the boot screen stopped waiting on, which never fires OnSyncServerFinish.
|
|
96
|
+
const isFirstSyncDone = useBootStore((state) => state.isFirstSyncDone);
|
|
97
|
+
const wasFirstSyncDoneRef = useRef(isFirstSyncDone);
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
const justSynced = isFirstSyncDone && !wasFirstSyncDoneRef.current;
|
|
100
|
+
wasFirstSyncDoneRef.current = isFirstSyncDone;
|
|
101
|
+
if (justSynced && queryEnabled)
|
|
102
|
+
void loadInitialRef.current();
|
|
103
|
+
}, [isFirstSyncDone, queryEnabled]);
|
|
73
104
|
const items = useMemo(() => conversationList.map(toConversationListItem), [conversationList]);
|
|
74
105
|
return {
|
|
75
106
|
items,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Marks the offline overlay, which stays interactive while the rest of the desk is locked. */
|
|
2
|
+
export declare const DESK_OFFLINE_OVERLAY_ATTR = "data-desk-offline-overlay";
|
|
3
|
+
/**
|
|
4
|
+
* While offline, makes every direct child of the workspace except the overlay inert (including
|
|
5
|
+
* ones added later) and turns shortcuts off. On reconnect it restores the previous focus,
|
|
6
|
+
* else `getFallbackFocus()`, so typing never lands on <body> where single-key shortcuts fire.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useDeskOfflineLock: (workspaceEl: HTMLElement | null, isOffline: boolean, getFallbackFocus: () => HTMLElement | null) => void;
|
|
9
|
+
//# sourceMappingURL=useDeskOfflineLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDeskOfflineLock.d.ts","sourceRoot":"","sources":["../../../src/hooks/conversation/useDeskOfflineLock.ts"],"names":[],"mappings":"AAGA,+FAA+F;AAC/F,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,WAAW,GAAG,IAAI,EAC/B,WAAW,OAAO,EAClB,kBAAkB,MAAM,WAAW,GAAG,IAAI,SAoC3C,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
/** Marks the offline overlay, which stays interactive while the rest of the desk is locked. */
|
|
4
|
+
export const DESK_OFFLINE_OVERLAY_ATTR = "data-desk-offline-overlay";
|
|
5
|
+
/**
|
|
6
|
+
* While offline, makes every direct child of the workspace except the overlay inert (including
|
|
7
|
+
* ones added later) and turns shortcuts off. On reconnect it restores the previous focus,
|
|
8
|
+
* else `getFallbackFocus()`, so typing never lands on <body> where single-key shortcuts fire.
|
|
9
|
+
*/
|
|
10
|
+
export const useDeskOfflineLock = (workspaceEl, isOffline, getFallbackFocus) => {
|
|
11
|
+
const fallbackRef = useRef(getFallbackFocus);
|
|
12
|
+
fallbackRef.current = getFallbackFocus;
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!workspaceEl || !isOffline)
|
|
15
|
+
return;
|
|
16
|
+
const active = document.activeElement;
|
|
17
|
+
const previousFocus = active instanceof HTMLElement && workspaceEl.contains(active) ? active : null;
|
|
18
|
+
const locked = new Set();
|
|
19
|
+
const lockChildren = () => {
|
|
20
|
+
Array.from(workspaceEl.children).forEach((child) => {
|
|
21
|
+
if (child.hasAttribute(DESK_OFFLINE_OVERLAY_ATTR) || locked.has(child))
|
|
22
|
+
return;
|
|
23
|
+
child.setAttribute("inert", "");
|
|
24
|
+
locked.add(child);
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
lockChildren();
|
|
28
|
+
const observer = new MutationObserver(lockChildren);
|
|
29
|
+
observer.observe(workspaceEl, { childList: true });
|
|
30
|
+
workspaceEl.setAttribute("data-desk-shortcuts-off", "");
|
|
31
|
+
return () => {
|
|
32
|
+
observer.disconnect();
|
|
33
|
+
locked.forEach((child) => child.removeAttribute("inert"));
|
|
34
|
+
workspaceEl.removeAttribute("data-desk-shortcuts-off");
|
|
35
|
+
if (document.activeElement && document.activeElement !== document.body)
|
|
36
|
+
return;
|
|
37
|
+
for (const target of [previousFocus, fallbackRef.current()]) {
|
|
38
|
+
if (!(target === null || target === void 0 ? void 0 : target.isConnected))
|
|
39
|
+
continue;
|
|
40
|
+
target.focus();
|
|
41
|
+
if (document.activeElement === target)
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}, [workspaceEl, isOffline]);
|
|
46
|
+
};
|