@droppii-org/chat-sdk 0.3.6 → 0.3.8

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.
Files changed (57) hide show
  1. package/dist/components/common/ConfirmModal.d.ts +18 -0
  2. package/dist/components/common/ConfirmModal.d.ts.map +1 -0
  3. package/dist/components/common/ConfirmModal.js +15 -0
  4. package/dist/components/common/InfoModal.d.ts +11 -0
  5. package/dist/components/common/InfoModal.d.ts.map +1 -0
  6. package/dist/components/common/InfoModal.js +13 -0
  7. package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
  8. package/dist/components/compose/ComposeNewMessageModal.js +4 -6
  9. package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
  10. package/dist/components/conversation/ConversationByGroupItem.js +3 -7
  11. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  12. package/dist/components/conversation/ConversationBySessionItem.js +3 -7
  13. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  14. package/dist/components/conversation/DeskConversationList.js +20 -14
  15. package/dist/components/conversation-inbox/ConversationInboxList.d.ts.map +1 -1
  16. package/dist/components/conversation-inbox/ConversationInboxList.js +4 -6
  17. package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
  18. package/dist/components/message/GroupMembersDrawer.js +8 -8
  19. package/dist/components/message/MessageList.d.ts.map +1 -1
  20. package/dist/components/message/MessageList.js +17 -3
  21. package/dist/components/message/item/MessageBubble.d.ts +3 -2
  22. package/dist/components/message/item/MessageBubble.d.ts.map +1 -1
  23. package/dist/components/message/item/MessageBubble.js +8 -2
  24. package/dist/components/message/item/index.d.ts.map +1 -1
  25. package/dist/components/message/item/index.js +23 -6
  26. package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
  27. package/dist/components/thread/GroupThreadInfo.js +43 -14
  28. package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -1
  29. package/dist/components/thread/SingleThreadInfo.js +3 -2
  30. package/dist/context/ChatContext.d.ts +1 -1
  31. package/dist/context/ChatContext.d.ts.map +1 -1
  32. package/dist/context/ChatContext.js +2 -1
  33. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  34. package/dist/hooks/global/useGlobalEvent.js +29 -4
  35. package/dist/hooks/group/useCreateGroup.js +5 -5
  36. package/dist/hooks/group/useDismissGroup.d.ts.map +1 -1
  37. package/dist/hooks/group/useDismissGroup.js +18 -0
  38. package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
  39. package/dist/hooks/session/useConversationSessionState.js +1 -0
  40. package/dist/layout/index.d.ts.map +1 -1
  41. package/dist/layout/index.js +13 -2
  42. package/dist/locales/vi/common.json +14 -8
  43. package/dist/screens/conversation-detail/index.d.ts.map +1 -1
  44. package/dist/screens/conversation-detail/index.js +6 -7
  45. package/dist/store/conversation.d.ts.map +1 -1
  46. package/dist/store/conversation.js +26 -5
  47. package/dist/store/type.d.ts +5 -0
  48. package/dist/store/type.d.ts.map +1 -1
  49. package/dist/styles/global.css +1 -1
  50. package/dist/types/chat.d.ts +2 -0
  51. package/dist/types/chat.d.ts.map +1 -1
  52. package/dist/utils/common.d.ts +1 -0
  53. package/dist/utils/common.d.ts.map +1 -1
  54. package/dist/utils/common.js +18 -0
  55. package/dist/utils/fileValidation.d.ts.map +1 -1
  56. package/dist/utils/fileValidation.js +1 -1
  57. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ConfirmModalProps {
3
+ open: boolean;
4
+ title: ReactNode;
5
+ okText: ReactNode;
6
+ cancelText: ReactNode;
7
+ onOk: () => void;
8
+ onCancel: () => void;
9
+ okType?: "danger" | "primary";
10
+ confirmLoading?: boolean;
11
+ okButtonProps?: {
12
+ disabled?: boolean;
13
+ };
14
+ children: ReactNode;
15
+ }
16
+ declare const ConfirmModal: ({ open, title, okText, cancelText, onOk, onCancel, okType, confirmLoading, okButtonProps, children, }: ConfirmModalProps) => import("react/jsx-runtime").JSX.Element;
17
+ export default ConfirmModal;
18
+ //# sourceMappingURL=ConfirmModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmModal.d.ts","sourceRoot":"","sources":["../../../src/components/common/ConfirmModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,QAAA,MAAM,YAAY,GAAI,uGAWnB,iBAAiB,4CAiCnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Modal } from "antd";
4
+ const ConfirmModal = ({ open, title, okText, cancelText, onOk, onCancel, okType = "primary", confirmLoading, okButtonProps, children, }) => {
5
+ return (_jsx(Modal, { centered: true, open: open, onOk: onOk, onCancel: onCancel, okText: okText, cancelText: cancelText, confirmLoading: confirmLoading, okButtonProps: Object.assign(Object.assign({}, okButtonProps), { className: okType === "danger"
6
+ ? "!h-10 !rounded-lg !border-danger-400 !bg-danger-400 !text-white hover:!border-danger-300 hover:!bg-danger-300"
7
+ : "!h-10 !rounded-lg !border-primary-400 !bg-primary-400 !text-white hover:!border-primary-300 hover:!bg-primary-300" }), cancelButtonProps: {
8
+ className: "!h-10 !rounded-lg !border-gray-25 !bg-gray-25 !text-gray-900",
9
+ }, styles: {
10
+ header: { textAlign: "center", marginBottom: 0 },
11
+ footer: { marginTop: 16 },
12
+ body: { paddingTop: 4 },
13
+ }, classNames: { footer: "border-t border-gray-25 pt-4" }, title: _jsx("span", { className: "text-[18px] font-bold text-gray-900", children: title }), getContainer: false, forceRender: true, children: children }));
14
+ };
15
+ export default ConfirmModal;
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+ export interface InfoModalProps {
3
+ open: boolean;
4
+ title: ReactNode;
5
+ okText: ReactNode;
6
+ onOk: () => void;
7
+ children: ReactNode;
8
+ }
9
+ declare const InfoModal: ({ open, title, okText, onOk, children }: InfoModalProps) => import("react/jsx-runtime").JSX.Element;
10
+ export default InfoModal;
11
+ //# sourceMappingURL=InfoModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InfoModal.d.ts","sourceRoot":"","sources":["../../../src/components/common/InfoModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,QAAA,MAAM,SAAS,GAAI,yCAAyC,cAAc,4CA0BzE,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Modal } from "antd";
4
+ const InfoModal = ({ open, title, okText, onOk, children }) => {
5
+ return (_jsx(Modal, { centered: true, open: open, onOk: onOk, onCancel: onOk, okText: okText, cancelButtonProps: { style: { display: "none" } }, okButtonProps: {
6
+ className: "!h-10 !w-full !rounded-lg !border-primary-400 !bg-primary-400 !text-white hover:!border-primary-300 hover:!bg-primary-300",
7
+ }, styles: {
8
+ header: { textAlign: "center", marginBottom: 0 },
9
+ footer: { marginTop: 16 },
10
+ body: { paddingTop: 4 },
11
+ }, classNames: { footer: "border-t border-gray-25 pt-4" }, title: _jsx("span", { className: "text-[18px] font-bold text-gray-900", children: title }), getContainer: false, forceRender: true, children: children }));
12
+ };
13
+ export default InfoModal;
@@ -1 +1 @@
1
- {"version":3,"file":"ComposeNewMessageModal.d.ts","sourceRoot":"","sources":["../../../src/components/compose/ComposeNewMessageModal.tsx"],"names":[],"mappings":"AAeA,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID,QAAA,MAAM,sBAAsB,GAAI,oBAG7B,2BAA2B,4CAwI7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"ComposeNewMessageModal.d.ts","sourceRoot":"","sources":["../../../src/components/compose/ComposeNewMessageModal.tsx"],"names":[],"mappings":"AAeA,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAID,QAAA,MAAM,sBAAsB,GAAI,oBAG7B,2BAA2B,4CAsI7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -3,7 +3,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useDebounce } from "ahooks";
4
4
  import { Avatar, Empty, Input, Modal, Spin } from "antd";
5
5
  import { useState } from "react";
6
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
7
6
  import { useTranslation } from "react-i18next";
8
7
  import { SessionType } from "@droppii-org/wasm-client-sdk";
9
8
  import { Icon } from "../../components/icon";
@@ -11,6 +10,7 @@ import { useEligibleAccounts } from "../../hooks/group/useEligibleAccounts";
11
10
  import { DChatSDK } from "../../constants/sdk";
12
11
  import useConversationStore from "../../store/conversation";
13
12
  import CreateGroupForm from "../../components/group/CreateGroupForm";
13
+ import { useChatContext } from "../../context/ChatContext";
14
14
  const ComposeNewMessageModal = ({ open, onClose, }) => {
15
15
  const { t } = useTranslation("conversation-inbox");
16
16
  const [step, setStep] = useState("compose");
@@ -18,9 +18,7 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
18
18
  const [openingUserID, setOpeningUserID] = useState(null);
19
19
  const debouncedSearch = useDebounce(search, { wait: 300 });
20
20
  const { accounts, isLoading } = useEligibleAccounts(debouncedSearch, open);
21
- const router = useRouter();
22
- const pathname = usePathname();
23
- const searchParams = useSearchParams();
21
+ const { onNavigate } = useChatContext();
24
22
  const setConversationData = useConversationStore((state) => state.setConversationData);
25
23
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
26
24
  const handleClose = () => {
@@ -44,12 +42,12 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
44
42
  });
45
43
  setConversationData(conversation);
46
44
  setSelectedConversationId(conversation.conversationID);
47
- const newSearchParams = new URLSearchParams(searchParams);
45
+ const newSearchParams = new URLSearchParams(window.location.search);
48
46
  newSearchParams.set("threadId", conversation.conversationID);
49
47
  newSearchParams.delete("userId");
50
48
  newSearchParams.delete("groupId");
51
49
  newSearchParams.delete("botId");
52
- router.push(`${pathname}?${newSearchParams.toString()}`);
50
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
53
51
  handleClose();
54
52
  }
55
53
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAOxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CA8D9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAOxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CA2D9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
4
3
  import { useTranslation } from "react-i18next";
5
4
  import useConversationStore from "../../store/conversation";
6
5
  import { useChatContext } from "../../context/ChatContext";
@@ -9,10 +8,7 @@ import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
9
8
  import ThreadCard from "./ThreadCard";
10
9
  const ConversationByGroupItem = ({ group, latestPreview, }) => {
11
10
  const { t } = useTranslation();
12
- const user = useChatContext().user;
13
- const router = useRouter();
14
- const pathname = usePathname();
15
- const searchParams = useSearchParams();
11
+ const { user, onNavigate } = useChatContext();
16
12
  const setConversationData = useConversationStore((state) => state.setConversationData);
17
13
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
18
14
  const isSelected = useConversationStore((state) => state.selectedConversationId === group.conversationId);
@@ -20,12 +16,12 @@ const ConversationByGroupItem = ({ group, latestPreview, }) => {
20
16
  const handleClick = () => {
21
17
  setConversationData(conversation);
22
18
  setSelectedConversationId(conversation.conversationID);
23
- const newSearchParams = new URLSearchParams(searchParams);
19
+ const newSearchParams = new URLSearchParams(window.location.search);
24
20
  newSearchParams.set("threadId", conversation.conversationID);
25
21
  newSearchParams.delete("userId");
26
22
  newSearchParams.delete("groupId");
27
23
  newSearchParams.delete("botId");
28
- router.push(`${pathname}?${newSearchParams.toString()}`);
24
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
29
25
  };
30
26
  const latestMsg = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsg) || "";
31
27
  const latestMsgSendTime = (latestPreview === null || latestPreview === void 0 ? void 0 : latestPreview.latestMsgSendTime) || 0;
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAWhD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDA6EhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
1
+ {"version":3,"file":"ConversationBySessionItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationBySessionItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAE7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUhD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDA0EhC,CAAC;AAEF,eAAe,yBAAyB,CAAC"}
@@ -2,7 +2,6 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import useConversationStore from "../../store/conversation";
5
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
6
5
  import { Avatar } from "antd";
7
6
  import { useChatContext } from "../../context/ChatContext";
8
7
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
@@ -13,26 +12,23 @@ import ThreadCard from "./ThreadCard";
13
12
  const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
14
13
  var _a;
15
14
  const { t } = useTranslation();
16
- const user = useChatContext().user;
15
+ const { user, onNavigate } = useChatContext();
17
16
  const isSelected = useConversationStore((state) => {
18
17
  var _a;
19
18
  return state.selectedConversationId ===
20
19
  ((_a = sessionItem === null || sessionItem === void 0 ? void 0 : sessionItem.conversation) === null || _a === void 0 ? void 0 : _a.conversationID);
21
20
  });
22
21
  const conversation = sessionItem === null || sessionItem === void 0 ? void 0 : sessionItem.conversation;
23
- const router = useRouter();
24
- const pathname = usePathname();
25
- const searchParams = useSearchParams();
26
22
  const setConversationData = useConversationStore((state) => state.setConversationData);
27
23
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
28
24
  const handleConversationClick = (conversation) => {
29
25
  setConversationData(conversation);
30
- const newSearchParams = new URLSearchParams(searchParams);
26
+ const newSearchParams = new URLSearchParams(window.location.search);
31
27
  newSearchParams.set("threadId", conversation.conversationID);
32
28
  newSearchParams.delete("userId");
33
29
  newSearchParams.delete("groupId");
34
30
  newSearchParams.delete("botId");
35
- router.push(`${pathname}?${newSearchParams.toString()}`);
31
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
36
32
  setSelectedConversationId(conversation.conversationID);
37
33
  };
38
34
  const { avatar, displayName = "" } = useConversationDisplayData(conversation || null);
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+CAgSzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+CAySzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -68,20 +68,26 @@ const DeskConversationList = () => {
68
68
  }, [showSearch]);
69
69
  useEffect(() => {
70
70
  const threadId = searchParams.get("threadId");
71
- if (threadId) {
72
- setSelectedConversationId(threadId);
73
- const selectedConversation = conversationList.find((conv) => conv.conversationID === threadId);
74
- if (selectedConversation) {
75
- setConversationData(selectedConversation);
76
- }
77
- else {
78
- DChatSDK.getMultipleConversation([threadId]).then((res) => {
79
- if (res.data.length > 0) {
80
- setConversationData(res.data[0]);
81
- updateConversationList(res.data, "filter");
82
- }
83
- });
84
- }
71
+ if (!threadId) {
72
+ useConversationStore.getState().clearSelectedConversation();
73
+ return;
74
+ }
75
+ setSelectedConversationId(threadId);
76
+ const selectedConversation = conversationList.find((conv) => conv.conversationID === threadId);
77
+ if (selectedConversation) {
78
+ setConversationData(selectedConversation);
79
+ }
80
+ else {
81
+ DChatSDK.getMultipleConversation([threadId]).then((res) => {
82
+ // threadId can go stale between the fetch starting and resolving
83
+ // (e.g. the user was removed from that group mid-flight) — only
84
+ // apply the result if the URL still points at this conversation.
85
+ if (res.data.length > 0 &&
86
+ searchParams.get("threadId") === threadId) {
87
+ setConversationData(res.data[0]);
88
+ updateConversationList(res.data, "filter");
89
+ }
90
+ });
85
91
  }
86
92
  }, [searchParams, conversationList]);
87
93
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationInboxList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxList.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAKjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAYD,QAAA,MAAM,qBAAqB,GAAI,oFAO5B,0BAA0B,4CAiF5B,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ConversationInboxList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxList.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAMjE,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACvD;AAYD,QAAA,MAAM,qBAAqB,GAAI,oFAO5B,0BAA0B,4CA+E5B,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -1,27 +1,25 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
4
3
  import { useTranslation } from "react-i18next";
5
4
  import { Empty, Spin } from "antd";
6
5
  import InfiniteScroll from "react-infinite-scroll-component";
7
6
  import useConversationStore from "../../store/conversation";
7
+ import { useChatContext } from "../../context/ChatContext";
8
8
  import { Icon } from "../../components/icon";
9
9
  import ConversationInboxItem from "./ConversationInboxItem";
10
10
  const FooterSkeleton = () => (_jsxs("div", { className: "flex items-center gap-3 p-3", children: [_jsx("div", { className: "h-10 w-10 animate-pulse rounded-full bg-neutral-200" }), _jsxs("div", { className: "flex flex-1 flex-col gap-2", children: [_jsx("div", { className: "h-3 w-1/2 animate-pulse rounded bg-neutral-200" }), _jsx("div", { className: "h-2.5 w-1/3 animate-pulse rounded bg-neutral-200" })] })] }));
11
11
  const ConversationInboxList = ({ items, isLoading, isFetchingNextPage, hasNextPage, onLoadMore, onSelectThread, }) => {
12
12
  const { t } = useTranslation("conversation-inbox");
13
- const router = useRouter();
14
- const pathname = usePathname();
15
- const searchParams = useSearchParams();
13
+ const { onNavigate } = useChatContext();
16
14
  const setConversationData = useConversationStore((state) => state.setConversationData);
17
15
  const setSelectedConversationId = useConversationStore((state) => state.setSelectedConversationId);
18
16
  const handleSelectThread = (item) => {
19
- const newSearchParams = new URLSearchParams(searchParams);
17
+ const newSearchParams = new URLSearchParams(window.location.search);
20
18
  newSearchParams.set("threadId", item.conversationID);
21
19
  newSearchParams.delete("userId");
22
20
  newSearchParams.delete("groupId");
23
21
  newSearchParams.delete("botId");
24
- router.push(`${pathname}?${newSearchParams.toString()}`);
22
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(`${window.location.pathname}?${newSearchParams.toString()}`);
25
23
  setSelectedConversationId(item.conversationID);
26
24
  setConversationData(item);
27
25
  onSelectThread === null || onSelectThread === void 0 ? void 0 : onSelectThread(item);
@@ -1 +1 @@
1
- {"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,UAAU,uBAAuB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA0CD,QAAA,MAAM,kBAAkB,GAAI,wDAKzB,uBAAuB,4CAwjBzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,UAAU,uBAAuB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA0CD,QAAA,MAAM,kBAAkB,GAAI,wDAKzB,uBAAuB,4CA2iBzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
3
- import { Avatar, Button, Drawer, Dropdown, Empty, Input, message, Modal, Spin } from "antd";
3
+ import { Avatar, Button, Drawer, Dropdown, Empty, Input, message, Spin } from "antd";
4
+ import ConfirmModal from "../../components/common/ConfirmModal";
4
5
  import { useEffect, useMemo, useState } from "react";
5
6
  import { useDebounce } from "ahooks";
6
7
  import { useTranslation } from "react-i18next";
7
- import { usePathname, useRouter } from "next/navigation";
8
+ import { useChatContext } from "../../context/ChatContext";
8
9
  import useAuthStore from "../../store/auth";
9
10
  import useConversationStore from "../../store/conversation";
10
11
  import { useGroupMembers } from "../../hooks/group/useGroupMembers";
@@ -33,8 +34,7 @@ const getMemberRoleLabel = ({ member, customerUserID, t, }) => {
33
34
  const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled = false, }) => {
34
35
  var _a, _b;
35
36
  const { t } = useTranslation();
36
- const router = useRouter();
37
- const pathname = usePathname();
37
+ const { onNavigate } = useChatContext();
38
38
  const [isOpen, setIsOpen] = useState(false);
39
39
  const [showLeaveConfirm, setShowLeaveConfirm] = useState(false);
40
40
  const [showAddMember, setShowAddMember] = useState(false);
@@ -238,7 +238,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
238
238
  try {
239
239
  await leaveConversation({ conversationId });
240
240
  message.success(t("leave_group_success"));
241
- router.replace(pathname);
241
+ onNavigate === null || onNavigate === void 0 ? void 0 : onNavigate(window.location.pathname);
242
242
  setShowLeaveConfirm(false);
243
243
  setIsOpen(false);
244
244
  }
@@ -360,11 +360,11 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
360
360
  userID: adminPermissionMember.userID,
361
361
  permissions: adminPermissionMember.permissions,
362
362
  }
363
- : undefined, onMemberPermissionsSaved: patchMemberPermissions })), _jsx(Modal, { centered: true, open: showLeaveConfirm, onOk: handleConfirmLeave, onCancel: () => setShowLeaveConfirm(false), title: t("leave_group_confirm_title"), okText: t("leave_group"), cancelText: t("cancel"), okType: "danger", confirmLoading: isLeaving, getContainer: false, forceRender: true, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(Modal, { centered: true, open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "remove", onOk: handleConfirmRemoveMember, onCancel: () => setConfirmMemberAction(null), title: t("remove_member_confirm_title"), okText: t("remove_from_group"), cancelText: t("cancel"), okType: "danger", getContainer: false, forceRender: true, children: _jsx("p", { children: t("remove_member_confirm_message", {
363
+ : undefined, onMemberPermissionsSaved: patchMemberPermissions })), _jsx(ConfirmModal, { open: showLeaveConfirm, onOk: handleConfirmLeave, onCancel: () => setShowLeaveConfirm(false), title: t("leave_group_confirm_title"), okText: t("leave_group"), cancelText: t("cancel"), okType: "danger", confirmLoading: isLeaving, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "remove", onOk: handleConfirmRemoveMember, onCancel: () => setConfirmMemberAction(null), title: t("remove_member_confirm_title"), okText: t("remove_from_group"), cancelText: t("cancel"), okType: "danger", children: _jsx("p", { children: t("remove_member_confirm_message", {
364
364
  name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
365
- }) }) }), _jsx(Modal, { centered: true, open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "transfer-owner", onOk: handleConfirmTransferOwner, onCancel: () => setConfirmMemberAction(null), title: t("transfer_owner_confirm_title"), okText: t("transfer_owner"), cancelText: t("cancel"), okType: "danger", getContainer: false, forceRender: true, children: _jsx("p", { children: t("transfer_owner_confirm_message", {
365
+ }) }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "transfer-owner", onOk: handleConfirmTransferOwner, onCancel: () => setConfirmMemberAction(null), title: t("transfer_owner_confirm_title"), okText: t("transfer_owner"), cancelText: t("cancel"), okType: "danger", children: _jsx("p", { children: t("transfer_owner_confirm_message", {
366
366
  name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
367
- }) }) }), _jsx(Modal, { centered: true, open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "assign-admin", onOk: handleConfirmAssignAdmin, onCancel: () => setConfirmMemberAction(null), title: t("assign_admin_confirm_title"), okText: t("agree"), cancelText: t("cancel"), getContainer: false, forceRender: true, children: _jsx("p", { children: t("assign_admin_confirm_message", {
367
+ }) }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "assign-admin", onOk: handleConfirmAssignAdmin, onCancel: () => setConfirmMemberAction(null), title: t("assign_admin_confirm_title"), okText: t("agree"), cancelText: t("cancel"), children: _jsx("p", { children: t("assign_admin_confirm_message", {
368
368
  name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
369
369
  }) }) })] }));
370
370
  };
@@ -1 +1 @@
1
- {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AA8BA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAmR3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAgCA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAgS3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useMessage } from "../../hooks/message/useMessage";
4
- import { Button, Empty, Modal, Spin, message as toastMessage } from "antd";
4
+ import { Button, Empty, Spin, message as toastMessage } from "antd";
5
5
  import { useCallback, useEffect, useState } from "react";
6
6
  import dayjs from "dayjs";
7
7
  import isToday from "dayjs/plugin/isToday";
8
+ import { GroupMemberRole, GroupStatus } from "@droppii-org/wasm-client-sdk";
8
9
  import emitter from "../../utils/events";
9
10
  import MessageItem from "./item";
10
11
  import MessageInfiniteScroll from "./MessageInfiniteScroll";
@@ -24,6 +25,7 @@ import { useGroupPermission } from "../../hooks/group/useGroupPermission";
24
25
  import { isGroupSession } from "../../utils/imCommon";
25
26
  import { ConnectStatus, SessionStatus } from "../../types/chat";
26
27
  import PinLimitModal from "../../components/conversation-detail/PinLimitModal";
28
+ import ConfirmModal from "../../components/common/ConfirmModal";
27
29
  dayjs.extend(isToday);
28
30
  const MessageList = (props) => {
29
31
  const { t } = useTranslation();
@@ -32,7 +34,19 @@ const MessageList = (props) => {
32
34
  const conversationData = useConversationStore((state) => state.conversationData);
33
35
  const permissions = useGroupPermission(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID);
34
36
  const isGroupChat = isGroupSession(conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType);
35
- const canSendMessage = !isGroupChat || permissions.sendMessage;
37
+ const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
38
+ const currentMemberInGroup = useConversationStore((state) => state.currentMemberInGroup);
39
+ // Group/member data refetches async on conversation switch and can briefly
40
+ // hold the previous conversation's groupID or a not-yet-loaded roleLevel.
41
+ const isGroupStateFresh = !isGroupChat ||
42
+ ((currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupID) === (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) &&
43
+ (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.groupID) === (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) &&
44
+ (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) !== undefined);
45
+ // Group-wide mute (GroupThreadInfo.tsx) restricts Normal members only.
46
+ const isRestrictedByGroupMute = isGroupChat &&
47
+ (currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status) === GroupStatus.Muted &&
48
+ (currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Normal;
49
+ const canSendMessage = !isGroupChat || (permissions.sendMessage && !isRestrictedByGroupMute);
36
50
  const canPinMessage = !isGroupChat || permissions.pinMessage;
37
51
  const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
38
52
  const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
@@ -136,6 +150,6 @@ const MessageList = (props) => {
136
150
  if (!conversationData) {
137
151
  return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
138
152
  }
139
- return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t("send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(Modal, { centered: true, open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, getContainer: false, forceRender: true, children: _jsx("p", { children: t("revoke_message_confirm_message") }) }), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
153
+ return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t(isRestrictedByGroupMute ? "send_message_disabled_group_mute" : "send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(ConfirmModal, { open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, children: _jsx("p", { children: t("revoke_message_confirm_message") }) }), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
140
154
  };
141
155
  export default MessageList;
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from "react";
1
+ import { type ReactNode } from "react";
2
2
  export interface MessageBubbleProps {
3
3
  isMine: boolean;
4
4
  content: ReactNode;
@@ -8,7 +8,8 @@ export interface MessageBubbleProps {
8
8
  isInternal?: boolean;
9
9
  id?: string;
10
10
  isTransparent?: boolean;
11
+ timeLabel?: string;
11
12
  }
12
- declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
13
+ declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, timeLabel, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
13
14
  export default MessageBubble;
14
15
  //# sourceMappingURL=MessageBubble.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA4CD,QAAA,MAAM,aAAa,GAAI,0FASpB,kBAAkB,4CA6CpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4CD,QAAA,MAAM,aAAa,GAAI,qGAUpB,kBAAkB,4CAiEpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import clsx from "clsx";
4
+ import { useState } from "react";
4
5
  const INCOMING_TAIL_PATH = "M1.3798 18H4V0C3.51762 4.5 3.34269 10.5 0.966518 14C-0.568384 16.2608 -0.159364 18 1.3798 18Z";
5
6
  const OUTGOING_TAIL_PATH = "M2.6202 18H0V0C0.482375 4.5 0.657308 10.5 3.03348 14C4.56838 16.2608 4.15936 18 2.6202 18Z";
6
7
  const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className: clsx("flex shrink-0 items-center justify-center", isInternal
@@ -8,8 +9,10 @@ const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className:
8
9
  : isMine
9
10
  ? "text-chat-bubble-mine"
10
11
  : "text-chat-bubble-neutral"), children: _jsx("div", { className: "flex-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "4", height: "18", viewBox: "0 0 4 18", fill: "none", "aria-hidden": true, children: _jsx("path", { d: isMine ? OUTGOING_TAIL_PATH : INCOMING_TAIL_PATH, fill: "currentColor", fillOpacity: "0.1" }) }) }) }));
11
- const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, }) => {
12
+ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, timeLabel, }) => {
12
13
  const showTail = showTip && !isTransparent;
14
+ const [timeRevealed, setTimeRevealed] = useState(false);
15
+ const showTimeLabel = Boolean(timeLabel) && (showTip || timeRevealed);
13
16
  const bubbleClass = clsx("relative max-w-full", isTransparent
14
17
  ? "bg-transparent p-0"
15
18
  : clsx("p-2", showTip
@@ -21,6 +24,9 @@ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip =
21
24
  : isMine
22
25
  ? "bg-chat-bubble-mine/10"
23
26
  : "bg-chat-bubble-neutral/10"));
24
- return (_jsxs("div", { className: "relative flex items-end", "data-testid": "message-bubble", children: [!isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), _jsx("div", { className: bubbleClass, id: id, children: _jsxs("div", { className: clsx("flex flex-col gap-1", !isTransparent && "px-1"), children: [showSenderName && senderName && (_jsx("span", { className: "text-[12px] font-medium leading-[160%] tracking-[0.12px] text-gray-400", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal }))] }));
27
+ return (_jsxs("div", { className: "relative flex items-end", "data-testid": "message-bubble", onClick: () => {
28
+ if (!showTip && timeLabel)
29
+ setTimeRevealed((prev) => !prev);
30
+ }, children: [!isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), _jsx("div", { className: bubbleClass, id: id, children: _jsxs("div", { className: clsx("flex flex-col gap-1", !isTransparent && "px-1"), children: [showSenderName && senderName && (_jsx("span", { className: "text-[12px] font-medium leading-[160%] tracking-[0.12px] text-gray-400", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), showTimeLabel && (_jsx("span", { "data-testid": "message-time-label", className: clsx("absolute -bottom-2 flex h-[19px] w-[30px] items-center justify-center rounded-full bg-white text-[10px] text-gray-400 shadow-sm", isMine ? "right-1" : "left-1"), children: timeLabel }))] }));
25
31
  };
26
32
  export default MessageBubble;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AAkCtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAoBD,QAAA,MAAM,WAAW,GAAI,qMAalB,gBAAgB,mDA8RlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AAmCtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAyBD,QAAA,MAAM,WAAW,GAAI,qMAalB,gBAAgB,mDA+SlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -8,7 +8,8 @@ import MessageBubble from "./MessageBubble";
8
8
  import { renderBubbleContent } from "./renderMessageContent";
9
9
  import { getVisibleNeighbor, isMessageHiddenForViewer, } from "../../../hooks/message/useMessage";
10
10
  import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../../constants";
11
- import { formatTimestamp } from "../../../utils/common";
11
+ import { formatDividerTimestamp } from "../../../utils/common";
12
+ import { CustomMessageType } from "../../../types/customMessage";
12
13
  import useAuthStore from "../../../store/auth";
13
14
  import useConversationStore from "../../../store/conversation";
14
15
  import { useMemo, useRef } from "react";
@@ -22,6 +23,9 @@ import GroupNotificationMessageItem from "./GroupNotificationMessage";
22
23
  import { isGroupNotificationMessageType, isSystemLogMessageType } from "../../../utils/messageLog";
23
24
  import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessageType, isPinnableMessageType, MessageToolboxActionKey, MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, useMessageToolboxVisibility, } from "../../../components/message/toolbox";
24
25
  const MESSAGE_AVATAR_SIZE = 24;
26
+ const DIVIDER_GAP_MINUTES = 30;
27
+ const GROUPING_GAP_MINUTES = 5;
28
+ const isOrderMessage = (message) => { var _a; return ((_a = message === null || message === void 0 ? void 0 : message.customElem) === null || _a === void 0 ? void 0 : _a.extension) === CustomMessageType.Order; };
25
29
  const MessageAvatar = ({ avatarUrl, senderNickname, }) => {
26
30
  var _a;
27
31
  return (_jsx(Avatar, { size: MESSAGE_AVATAR_SIZE, src: avatarUrl, className: "shrink-0 self-end", children: ((_a = senderNickname === null || senderNickname === void 0 ? void 0 : senderNickname.charAt) === null || _a === void 0 ? void 0 : _a.call(senderNickname, 0)) || "A" }));
@@ -127,10 +131,23 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
127
131
  message.clientMsgID;
128
132
  const previousMessage = getVisibleNeighbor(allMessages, message, "prev");
129
133
  const nextMessage = getVisibleNeighbor(allMessages, message, "next");
130
- const prevSameUser = (previousMessage === null || previousMessage === void 0 ? void 0 : previousMessage.sendID) === (message === null || message === void 0 ? void 0 : message.sendID);
131
- const nextSameUser = (nextMessage === null || nextMessage === void 0 ? void 0 : nextMessage.sendID) === (message === null || message === void 0 ? void 0 : message.sendID);
134
+ const isOrder = isOrderMessage(message);
135
+ const prevSameUser = !isOrder &&
136
+ !!previousMessage &&
137
+ !isOrderMessage(previousMessage) &&
138
+ previousMessage.sendID === (message === null || message === void 0 ? void 0 : message.sendID) &&
139
+ Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "minute")) <=
140
+ GROUPING_GAP_MINUTES;
141
+ const nextSameUser = !isOrder &&
142
+ !!nextMessage &&
143
+ !isOrderMessage(nextMessage) &&
144
+ nextMessage.sendID === (message === null || message === void 0 ? void 0 : message.sendID) &&
145
+ Math.abs(dayjs(nextMessage.sendTime).diff(message.sendTime, "minute")) <=
146
+ GROUPING_GAP_MINUTES;
132
147
  const showTimeBreak = !previousMessage ||
133
- !dayjs(message.sendTime).isSame(previousMessage.sendTime, "days");
148
+ !dayjs(message.sendTime).isSame(previousMessage.sendTime, "days") ||
149
+ Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "minute")) >
150
+ DIVIDER_GAP_MINUTES;
134
151
  const showPeerInfo = isGroupChat || showSenderInfo;
135
152
  const showAvatar = showPeerInfo && !isMine && !nextSameUser;
136
153
  const showSenderName = showPeerInfo && !isMine && !prevSameUser;
@@ -142,7 +159,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
142
159
  className: "flex flex-col gap-2 py-1 px-3 sm:px-4 min-w-0",
143
160
  id: `${MSG_ITEM_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`,
144
161
  };
145
- const timeBreakNode = showTimeBreak ? (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "rounded-full bg-neutral-100 px-2 py-1 text-center text-xs text-gray-600", children: formatTimestamp(message.sendTime, { hasTime: true }) }) })) : null;
162
+ const timeBreakNode = showTimeBreak ? (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "rounded-full bg-neutral-100 px-2 py-1 text-center text-xs text-gray-600", children: formatDividerTimestamp(message.sendTime) }) })) : null;
146
163
  if (isSystemLogMessage) {
147
164
  return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
148
165
  timeBreakNode,
@@ -153,7 +170,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
153
170
  timeBreakNode,
154
171
  _jsx(GroupNotificationMessageItem, { message: message })));
155
172
  }
156
- const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: renderBubbleContent(message, onPressQuoteMessage), isTransparent: isStickerMessage }));
173
+ const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: renderBubbleContent(message, onPressQuoteMessage), isTransparent: isStickerMessage, timeLabel: dayjs.tz(message.sendTime).format("HH:mm") }));
157
174
  const showPinnedBadge = Boolean(message === null || message === void 0 ? void 0 : message.isPinned);
158
175
  const pinnedBadgeNode = showPinnedBadge ? (_jsx("span", { "data-testid": "message-pinned-badge", className: clsx("absolute -top-2.5 z-[1] flex h-[22px] w-[26px] items-center justify-center rounded-full border border-white bg-gray-25", isMine ? "left-0" : "right-0"), children: _jsx(Icon, { icon: "thumbtack-b", size: 12, className: "text-primary-400" }) })) : null;
159
176
  const wrappedBubble = hasContextMenu ? (_jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: ["contextMenu"], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, overlayClassName: MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, children: _jsxs("div", { ref: bubbleWrapperRef, className: "relative min-w-0", children: [bubbleNode, pinnedBadgeNode] }) })) : (_jsxs("div", { className: "relative min-w-0", children: [bubbleNode, pinnedBadgeNode] }));
@@ -1 +1 @@
1
- {"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"AAmBA,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;AAID,QAAA,MAAM,eAAe,GAAI,qBAAqB,oBAAoB,mDAigBjE,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"AAoBA,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;AAID,QAAA,MAAM,eAAe,GAAI,qBAAqB,oBAAoB,mDAyjBjE,CAAC;AAEF,eAAe,eAAe,CAAC"}