@droppii-org/chat-sdk 0.1.97 → 0.2.1

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 (138) hide show
  1. package/dist/assets/images/muteNotificationWarning.png +0 -0
  2. package/dist/components/compose/ComposeNewMessageModal.d.ts.map +1 -1
  3. package/dist/components/compose/ComposeNewMessageModal.js +1 -1
  4. package/dist/components/conversation/SwipeActionButton.d.ts +11 -0
  5. package/dist/components/conversation/SwipeActionButton.d.ts.map +1 -0
  6. package/dist/components/conversation/SwipeActionButton.js +7 -0
  7. package/dist/components/conversation/SwipeableRow.d.ts +10 -0
  8. package/dist/components/conversation/SwipeableRow.d.ts.map +1 -0
  9. package/dist/components/conversation/SwipeableRow.js +62 -0
  10. package/dist/components/conversation/ThreadCard.d.ts +2 -1
  11. package/dist/components/conversation/ThreadCard.d.ts.map +1 -1
  12. package/dist/components/conversation/ThreadCard.js +2 -2
  13. package/dist/components/conversation-inbox/ConversationInboxItem.d.ts.map +1 -1
  14. package/dist/components/conversation-inbox/ConversationInboxItem.js +14 -1
  15. package/dist/components/conversation-inbox/ConversationMuteSwipeAction.d.ts +8 -0
  16. package/dist/components/conversation-inbox/ConversationMuteSwipeAction.d.ts.map +1 -0
  17. package/dist/components/conversation-inbox/ConversationMuteSwipeAction.js +31 -0
  18. package/dist/components/conversation-inbox/MuteConversationConfirmModal.d.ts +9 -0
  19. package/dist/components/conversation-inbox/MuteConversationConfirmModal.d.ts.map +1 -0
  20. package/dist/components/conversation-inbox/MuteConversationConfirmModal.js +13 -0
  21. package/dist/components/conversation-inbox/index.d.ts +4 -0
  22. package/dist/components/conversation-inbox/index.d.ts.map +1 -1
  23. package/dist/components/conversation-inbox/index.js +2 -0
  24. package/dist/components/group/AddMemberModal.d.ts +9 -0
  25. package/dist/components/group/AddMemberModal.d.ts.map +1 -0
  26. package/dist/components/group/AddMemberModal.js +70 -0
  27. package/dist/components/group/AdminPermissionModal.d.ts +9 -0
  28. package/dist/components/group/AdminPermissionModal.d.ts.map +1 -0
  29. package/dist/components/group/AdminPermissionModal.js +63 -0
  30. package/dist/components/group/CreateGroupForm.d.ts +1 -2
  31. package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
  32. package/dist/components/group/CreateGroupForm.js +4 -4
  33. package/dist/components/group/GroupMemberPicker.d.ts.map +1 -1
  34. package/dist/components/group/GroupMemberPicker.js +16 -8
  35. package/dist/components/group/MemberPermissionPanel.d.ts +8 -0
  36. package/dist/components/group/MemberPermissionPanel.d.ts.map +1 -0
  37. package/dist/components/group/MemberPermissionPanel.js +53 -0
  38. package/dist/components/media-resources/FileResourceList.js +1 -1
  39. package/dist/components/media-resources/LinkResourceItem.js +1 -1
  40. package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
  41. package/dist/components/message/GroupMembersDrawer.js +210 -28
  42. package/dist/components/message/MessageHeader.d.ts +2 -1
  43. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  44. package/dist/components/message/MessageHeader.js +7 -2
  45. package/dist/components/message/MessageList.d.ts +1 -0
  46. package/dist/components/message/MessageList.d.ts.map +1 -1
  47. package/dist/components/message/MessageList.js +7 -2
  48. package/dist/components/message/SelectSession.d.ts.map +1 -1
  49. package/dist/components/message/SelectSession.js +3 -3
  50. package/dist/components/session/DeskTeamInbox.js +1 -1
  51. package/dist/components/thread/GroupThreadInfo.d.ts +11 -0
  52. package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -0
  53. package/dist/components/thread/GroupThreadInfo.js +156 -0
  54. package/dist/components/thread/ManualAssignPopover.js +2 -2
  55. package/dist/components/thread/SessionSection.d.ts.map +1 -1
  56. package/dist/components/thread/SessionSection.js +2 -8
  57. package/dist/components/thread/SingleThreadInfo.d.ts +6 -0
  58. package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -0
  59. package/dist/components/thread/SingleThreadInfo.js +70 -0
  60. package/dist/components/thread/ThreadInfo.d.ts +7 -1
  61. package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
  62. package/dist/components/thread/ThreadInfo.js +14 -2
  63. package/dist/constants/images.d.ts +1 -0
  64. package/dist/constants/images.d.ts.map +1 -1
  65. package/dist/constants/images.js +2 -0
  66. package/dist/constants/index.d.ts +1 -0
  67. package/dist/constants/index.d.ts.map +1 -1
  68. package/dist/constants/index.js +3 -0
  69. package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
  70. package/dist/hooks/global/useGlobalEvent.js +27 -0
  71. package/dist/hooks/group/useAddGroupMember.d.ts +7 -0
  72. package/dist/hooks/group/useAddGroupMember.d.ts.map +1 -0
  73. package/dist/hooks/group/useAddGroupMember.js +20 -0
  74. package/dist/hooks/group/useDismissGroup.d.ts +4 -0
  75. package/dist/hooks/group/useDismissGroup.d.ts.map +1 -0
  76. package/dist/hooks/group/useDismissGroup.js +22 -0
  77. package/dist/hooks/group/useEligibleAccounts.d.ts +3 -0
  78. package/dist/hooks/group/useEligibleAccounts.d.ts.map +1 -1
  79. package/dist/hooks/group/useEligibleAccounts.js +9 -9
  80. package/dist/hooks/group/useEligibleAccountsForGroup.d.ts +9 -0
  81. package/dist/hooks/group/useEligibleAccountsForGroup.d.ts.map +1 -0
  82. package/dist/hooks/group/useEligibleAccountsForGroup.js +26 -0
  83. package/dist/hooks/group/useGetGroupCandidates.d.ts +7 -2
  84. package/dist/hooks/group/useGetGroupCandidates.d.ts.map +1 -1
  85. package/dist/hooks/group/useGetGroupCandidates.js +33 -16
  86. package/dist/hooks/group/useGroupMemberActions.d.ts +25 -0
  87. package/dist/hooks/group/useGroupMemberActions.d.ts.map +1 -0
  88. package/dist/hooks/group/useGroupMemberActions.js +54 -0
  89. package/dist/hooks/group/useGroupPermission.d.ts +15 -0
  90. package/dist/hooks/group/useGroupPermission.d.ts.map +1 -0
  91. package/dist/hooks/group/useGroupPermission.js +70 -0
  92. package/dist/hooks/group/useSearchGroupMembers.d.ts +8 -0
  93. package/dist/hooks/group/useSearchGroupMembers.d.ts.map +1 -0
  94. package/dist/hooks/group/useSearchGroupMembers.js +37 -0
  95. package/dist/hooks/group/useSetMemberPermission.d.ts +12 -0
  96. package/dist/hooks/group/useSetMemberPermission.d.ts.map +1 -0
  97. package/dist/hooks/group/useSetMemberPermission.js +32 -0
  98. package/dist/hooks/group/useToggleGroupSendMessage.d.ts +7 -0
  99. package/dist/hooks/group/useToggleGroupSendMessage.d.ts.map +1 -0
  100. package/dist/hooks/group/useToggleGroupSendMessage.js +22 -0
  101. package/dist/hooks/group/useUpdateGroupInfo.d.ts +7 -0
  102. package/dist/hooks/group/useUpdateGroupInfo.d.ts.map +1 -0
  103. package/dist/hooks/group/useUpdateGroupInfo.js +15 -0
  104. package/dist/hooks/session/useBlockUser.d.ts +10 -0
  105. package/dist/hooks/session/useBlockUser.d.ts.map +1 -0
  106. package/dist/hooks/session/useBlockUser.js +49 -0
  107. package/dist/hooks/session/useDeleteConversation.d.ts +4 -0
  108. package/dist/hooks/session/useDeleteConversation.d.ts.map +1 -0
  109. package/dist/hooks/session/useDeleteConversation.js +20 -0
  110. package/dist/hooks/session/useGetListSessionByConversation.d.ts +2 -1
  111. package/dist/hooks/session/useGetListSessionByConversation.d.ts.map +1 -1
  112. package/dist/hooks/session/useGetListSessionByConversation.js +2 -2
  113. package/dist/hooks/session/useMuteConversation.d.ts +8 -0
  114. package/dist/hooks/session/useMuteConversation.d.ts.map +1 -0
  115. package/dist/hooks/session/useMuteConversation.js +37 -0
  116. package/dist/hooks/session/useToggleConversationNotification.d.ts +7 -0
  117. package/dist/hooks/session/useToggleConversationNotification.d.ts.map +1 -0
  118. package/dist/hooks/session/useToggleConversationNotification.js +15 -0
  119. package/dist/index.d.ts +2 -0
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +2 -0
  122. package/dist/locales/vi/common.json +63 -1
  123. package/dist/locales/vi/conversation-inbox.json +22 -0
  124. package/dist/screens/desk-message/index.d.ts +3 -1
  125. package/dist/screens/desk-message/index.d.ts.map +1 -1
  126. package/dist/screens/desk-message/index.js +4 -5
  127. package/dist/services/query.d.ts +2 -0
  128. package/dist/services/query.d.ts.map +1 -1
  129. package/dist/services/query.js +2 -0
  130. package/dist/store/groupPermissionOverrides.d.ts +23 -0
  131. package/dist/store/groupPermissionOverrides.d.ts.map +1 -0
  132. package/dist/store/groupPermissionOverrides.js +12 -0
  133. package/dist/styles/global.css +1 -1
  134. package/dist/utils/events.d.ts +1 -0
  135. package/dist/utils/events.d.ts.map +1 -1
  136. package/dist/utils/imCommon.d.ts +1 -0
  137. package/dist/utils/imCommon.d.ts.map +1 -1
  138. package/package.json +3 -2
@@ -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,4CAyI7B,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,4CAwI7B,CAAC;AAEF,eAAe,sBAAsB,CAAC"}
@@ -56,6 +56,6 @@ const ComposeNewMessageModal = ({ open, onClose, }) => {
56
56
  setOpeningUserID(null);
57
57
  }
58
58
  };
59
- return (_jsx(Modal, { title: step === "create-group" ? t("create_group.title") : t("compose.title"), open: open, onCancel: handleClose, afterClose: handleAfterClose, footer: null, centered: true, forceRender: true, width: step === "create-group" ? 720 : undefined, getContainer: false, children: step === "compose" ? (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Input, { size: "large", value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("compose.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }) }), _jsxs("button", { type: "button", className: "flex items-center gap-3 rounded-xl bg-primary-50 px-4 py-3.5 text-left transition-colors hover:bg-primary-100", onClick: () => setStep("create-group"), children: [_jsx("span", { className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-white", children: _jsx(Icon, { icon: "user-three-b", size: 22, className: "text-primary-400" }) }), _jsx("span", { className: "flex-1 text-base font-semibold text-gray-900", children: t("compose.create_group") }), _jsx(Icon, { icon: "angle-right-o", size: 18, className: "text-gray-400" })] }), !debouncedSearch && (_jsxs("div", { className: "flex items-center gap-2 px-1", children: [_jsx("span", { className: "shrink-0 text-xs font-semibold uppercase tracking-wide text-gray-400", children: t("compose.recent_suggestions") }), _jsx("span", { className: "h-px flex-1 bg-gray-100" })] })), _jsx("div", { className: "max-h-80 overflow-y-auto", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("compose.no_member_found") })) : (accounts.map((account) => (_jsxs("div", { className: "flex cursor-pointer items-center gap-3 border-b border-gray-50 px-1 py-3 transition-colors last:border-b-0 hover:bg-gray-50", onClick: () => handleOpenDirectMessage(account), children: [_jsx(Avatar, { src: account.avatar, size: 44, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), openingUserID === account.userID && _jsx(Spin, { size: "small" })] }, account.userID)))) })] })) : (_jsx(CreateGroupForm, { onBack: () => setStep("compose"), onDone: handleClose })) }));
59
+ return (_jsx(Modal, { title: step === "create-group" ? t("create_group.title") : t("compose.title"), open: open, onCancel: handleClose, afterClose: handleAfterClose, footer: null, centered: true, forceRender: true, width: step === "create-group" ? 720 : undefined, getContainer: false, children: step === "compose" ? (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(Input, { size: "large", value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("compose.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 18, className: "text-gray-400" }) }), _jsxs("button", { type: "button", className: "flex items-center gap-3 rounded-xl bg-primary-50 px-4 py-3.5 text-left transition-colors hover:bg-primary-100", onClick: () => setStep("create-group"), children: [_jsx("span", { className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-white", children: _jsx(Icon, { icon: "user-three-b", size: 22, className: "text-primary-400" }) }), _jsx("span", { className: "flex-1 text-base font-semibold text-gray-900", children: t("compose.create_group") }), _jsx(Icon, { icon: "angle-right-o", size: 18, className: "text-gray-400" })] }), !debouncedSearch && (_jsxs("div", { className: "flex items-center gap-2 px-1", children: [_jsx("span", { className: "shrink-0 text-xs font-semibold uppercase tracking-wide text-gray-400", children: t("compose.recent_suggestions") }), _jsx("span", { className: "h-px flex-1 bg-gray-100" })] })), _jsx("div", { className: "max-h-80 overflow-y-auto", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("compose.no_member_found") })) : (accounts.map((account) => (_jsxs("div", { className: "flex cursor-pointer items-center gap-3 border-b border-gray-50 px-1 py-3 transition-colors last:border-b-0 hover:bg-[#f5f5f5]", onClick: () => handleOpenDirectMessage(account), children: [_jsx(Avatar, { src: account.avatar, size: 44, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), openingUserID === account.userID && _jsx(Spin, { size: "small" })] }, account.userID)))) })] })) : (_jsx(CreateGroupForm, { onDone: handleClose })) }));
60
60
  };
61
61
  export default ComposeNewMessageModal;
@@ -0,0 +1,11 @@
1
+ export declare const SWIPE_ACTION_WIDTH = 76;
2
+ export interface SwipeActionButtonProps {
3
+ icon: string;
4
+ label: string;
5
+ onClick: () => void;
6
+ className?: string;
7
+ testId?: string;
8
+ }
9
+ declare const SwipeActionButton: ({ icon, label, onClick, className, testId, }: SwipeActionButtonProps) => import("react/jsx-runtime").JSX.Element;
10
+ export default SwipeActionButton;
11
+ //# sourceMappingURL=SwipeActionButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwipeActionButton.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/SwipeActionButton.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,QAAA,MAAM,iBAAiB,GAAI,8CAMxB,sBAAsB,4CAgBxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import clsx from "clsx";
4
+ import { Icon } from "../../components/icon";
5
+ export const SWIPE_ACTION_WIDTH = 76;
6
+ const SwipeActionButton = ({ icon, label, onClick, className, testId, }) => (_jsxs("button", { type: "button", onClick: onClick, "data-testid": testId, className: clsx("flex h-full shrink-0 flex-col items-center justify-center gap-1 p-2 text-white", className), style: { width: SWIPE_ACTION_WIDTH }, children: [_jsx(Icon, { icon: icon, size: 16 }), _jsx("span", { className: "text-center text-[10px] font-[510] leading-[1.6]", children: label })] }));
7
+ export default SwipeActionButton;
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ export interface SwipeableRowProps {
3
+ children: ReactNode;
4
+ actions: (close: () => void) => ReactNode;
5
+ actionsWidth: number;
6
+ disabled?: boolean;
7
+ }
8
+ declare const SwipeableRow: ({ children, actions, actionsWidth, disabled, }: SwipeableRowProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default SwipeableRow;
10
+ //# sourceMappingURL=SwipeableRow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SwipeableRow.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/SwipeableRow.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,OAAO,CAAC;AAOnD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,IAAI,KAAK,SAAS,CAAC;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,QAAA,MAAM,YAAY,GAAI,gDAKnB,iBAAiB,4CA4FnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { useDrag } from "@use-gesture/react";
5
+ import clsx from "clsx";
6
+ const OPEN_THRESHOLD_RATIO = 0.5;
7
+ const DIRECTION_LOCK_DISTANCE = 8;
8
+ const SwipeableRow = ({ children, actions, actionsWidth, disabled = false, }) => {
9
+ const [offset, setOffset] = useState(0);
10
+ const [isDragging, setIsDragging] = useState(false);
11
+ const containerRef = useRef(null);
12
+ const shouldSuppressClickRef = useRef(false);
13
+ const close = () => setOffset(0);
14
+ useEffect(() => {
15
+ if (offset === 0)
16
+ return;
17
+ const handleOutsidePointerDown = (event) => {
18
+ const container = containerRef.current;
19
+ if (!container)
20
+ return;
21
+ if (container.contains(event.target))
22
+ return;
23
+ close();
24
+ };
25
+ document.addEventListener("pointerdown", handleOutsidePointerDown);
26
+ return () => document.removeEventListener("pointerdown", handleOutsidePointerDown);
27
+ }, [offset, close]);
28
+ const bind = useDrag(({ active, offset: [ox], last, tap }) => {
29
+ if (tap)
30
+ return;
31
+ setOffset(-ox);
32
+ if (active) {
33
+ setIsDragging(true);
34
+ shouldSuppressClickRef.current = true;
35
+ return;
36
+ }
37
+ setIsDragging(false);
38
+ if (last) {
39
+ setOffset((current) => current > actionsWidth * OPEN_THRESHOLD_RATIO ? actionsWidth : 0);
40
+ }
41
+ }, {
42
+ enabled: !disabled,
43
+ axis: "lock",
44
+ axisThreshold: { mouse: DIRECTION_LOCK_DISTANCE, touch: DIRECTION_LOCK_DISTANCE },
45
+ bounds: { left: -actionsWidth, right: 0 },
46
+ from: () => [-offset, 0],
47
+ filterTaps: true,
48
+ });
49
+ const isOpen = offset > 0;
50
+ const handleContentClickCapture = (event) => {
51
+ const wasDragging = shouldSuppressClickRef.current;
52
+ shouldSuppressClickRef.current = false;
53
+ if (!wasDragging && !isOpen)
54
+ return;
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ if (!wasDragging)
58
+ close();
59
+ };
60
+ return (_jsxs("div", Object.assign({ ref: containerRef }, bind(), { className: "relative w-full touch-pan-y overflow-hidden", children: [_jsx("div", { "aria-hidden": !isOpen, className: "absolute inset-y-0 right-0 flex", style: { width: actionsWidth }, children: actions(close) }), _jsx("div", { className: clsx("relative bg-white transition-transform duration-200 ease-out", isDragging && "select-none transition-none"), style: { transform: `translate3d(-${offset}px, 0, 0)` }, onClickCapture: handleContentClickCapture, children: children })] })));
61
+ };
62
+ export default SwipeableRow;
@@ -8,7 +8,8 @@ export interface ThreadCardProps {
8
8
  isSelected?: boolean;
9
9
  onClick: () => void;
10
10
  testId?: string;
11
+ muteIndicator?: ReactNode;
11
12
  }
12
- declare const ThreadCard: ({ avatar, title, preview, time, unreadCount, isSelected, onClick, testId, }: ThreadCardProps) => import("react/jsx-runtime").JSX.Element;
13
+ declare const ThreadCard: ({ avatar, title, preview, time, unreadCount, isSelected, onClick, testId, muteIndicator, }: ThreadCardProps) => import("react/jsx-runtime").JSX.Element;
13
14
  export default ThreadCard;
14
15
  //# sourceMappingURL=ThreadCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadCard.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ThreadCard.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAoBvC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,QAAA,MAAM,UAAU,GAAI,6EASjB,eAAe,4CA4CjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ThreadCard.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ThreadCard.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAoBvC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED,QAAA,MAAM,UAAU,GAAI,4FAUjB,eAAe,4CA6CjB,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -7,9 +7,9 @@ const UnreadBadge = ({ count }) => {
7
7
  const isWide = label.length > 1;
8
8
  return (_jsx("span", { className: clsx("inline-flex h-5 shrink-0 items-center justify-center rounded-[32px] border-2 border-white bg-gradient-danger-light px-[3px] text-center text-[12px] font-semibold leading-[160%] text-white", isWide ? "min-w-8" : "min-w-5"), children: label }));
9
9
  };
10
- const ThreadCard = ({ avatar, title, preview, time, unreadCount = 0, isSelected = false, onClick, testId, }) => {
10
+ const ThreadCard = ({ avatar, title, preview, time, unreadCount = 0, isSelected = false, onClick, testId, muteIndicator, }) => {
11
11
  const hasUnread = unreadCount > 0;
12
- return (_jsxs("button", { type: "button", onClick: onClick, "data-testid": testId, className: clsx("relative flex w-full shrink-0 items-start gap-2 border-b border-gray-25 p-3 text-left transition-colors hover:bg-gray-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-chat-ring", isSelected ? "bg-blue-50" : "bg-white"), children: [isSelected && (_jsx("span", { className: "absolute bottom-0 left-0 top-0 w-1 bg-blue-500" })), avatar, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex min-h-[25.6px] items-center justify-between gap-1", children: [_jsx("div", { className: "min-w-0 flex-1", children: title }), time && (_jsx("span", { className: "shrink-0 text-[12px] font-normal leading-[160%] text-gray-400 [font-feature-settings:'liga'_off,'clig'_off]", children: time }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: clsx("min-w-0 flex-1 truncate text-[14px] leading-[160%] [font-feature-settings:'liga'_off,'clig'_off]", hasUnread
12
+ return (_jsxs("button", { type: "button", onClick: onClick, "data-testid": testId, className: clsx("relative flex w-full shrink-0 items-start gap-2 border-b border-gray-25 p-3 text-left transition-colors hover:bg-[#f5f5f5] focus:outline-none focus-visible:ring-2 focus-visible:ring-chat-ring", isSelected ? "bg-blue-50" : "bg-white"), children: [isSelected && (_jsx("span", { className: "absolute bottom-0 left-0 top-0 w-1 bg-blue-500" })), avatar, _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsxs("div", { className: "flex min-h-[25.6px] items-center justify-between gap-1", children: [_jsx("div", { className: "min-w-0 flex-1", children: title }), muteIndicator, time && (_jsx("span", { className: "shrink-0 text-[12px] font-normal leading-[160%] text-gray-400 [font-feature-settings:'liga'_off,'clig'_off]", children: time }))] }), _jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("p", { className: clsx("min-w-0 flex-1 truncate text-[14px] leading-[160%] [font-feature-settings:'liga'_off,'clig'_off]", hasUnread
13
13
  ? "font-[510] text-black"
14
14
  : "font-normal tracking-[0.14px] text-gray-600"), children: preview }), hasUnread && _jsx(UnreadBadge, { count: unreadCount })] })] })] }));
15
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationInboxItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxItem.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAYjE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC/C;AAQD,QAAA,MAAM,qBAAqB,GAAI,mBAAmB,0BAA0B,4CA2C3E,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ConversationInboxItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationInboxItem.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAkBjE,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC/C;AAQD,QAAA,MAAM,qBAAqB,GAAI,mBAAmB,0BAA0B,4CA4E3E,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -6,9 +6,15 @@ import useAuthStore from "../../store/auth";
6
6
  import { formatTimestamp, parseLatestMessage } from "../../utils/common";
7
7
  import { resolvePeerBadgeLabel, resolveConversationDisplayName, } from "../../utils/conversation";
8
8
  import { useConversationAvatarUrl } from "../../hooks/conversation/useConversationAvatarUrl";
9
+ import { useIsJoinedGroup } from "../../hooks/session/useIsJoinedGroup";
10
+ import { isConversationMuted } from "../../hooks/session/useMuteConversation";
9
11
  import ThreadCard from "../../components/conversation/ThreadCard";
12
+ import SwipeableRow from "../../components/conversation/SwipeableRow";
13
+ import { SWIPE_ACTION_WIDTH } from "../../components/conversation/SwipeActionButton";
14
+ import { Icon } from "../../components/icon";
10
15
  import ConversationPeerAvatar from "./ConversationPeerAvatar";
11
16
  import ConversationInboxTitle from "./ConversationInboxTitle";
17
+ import ConversationMuteSwipeAction from "./ConversationMuteSwipeAction";
12
18
  const formatLatestMessageTime = (sendTime) => {
13
19
  if (!sendTime)
14
20
  return "";
@@ -26,6 +32,13 @@ const ConversationInboxItem = ({ item, onClick }) => {
26
32
  const latestMessagePreview = parseLatestMessage(item.latestMsg, currentUserId, tCommon, { peerType: item.peerType, showName: item.showName });
27
33
  const latestMessageTime = formatLatestMessageTime(item.latestMsgSendTime || null);
28
34
  const unreadCount = (_a = item.unreadCount) !== null && _a !== void 0 ? _a : 0;
29
- return (_jsx(ThreadCard, { testId: "conversation-inbox-thread-card", onClick: () => onClick(item), unreadCount: unreadCount, time: latestMessageTime, preview: latestMessagePreview, avatar: _jsx(ConversationPeerAvatar, { peerType: item.peerType, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), title: _jsx(ConversationInboxTitle, { peerType: item.peerType, displayName: displayName, hasUnread: unreadCount > 0 }) }));
35
+ const isMuted = isConversationMuted(item.recvMsgOpt);
36
+ const { isJoined } = useIsJoinedGroup(item.groupID);
37
+ const hasLostGroupAccess = Boolean(item.groupID) && isJoined !== true;
38
+ const threadCard = (_jsx(ThreadCard, { testId: "conversation-inbox-thread-card", onClick: () => onClick(item), unreadCount: unreadCount, time: latestMessageTime, preview: latestMessagePreview, muteIndicator: isMuted ? (_jsx(Icon, { icon: "bell-off-b", size: 16, "aria-label": t("thread_card.mute.muted_icon_label"), className: "shrink-0 text-gray-400" })) : null, avatar: _jsx(ConversationPeerAvatar, { peerType: item.peerType, displayName: displayName, avatarUrl: avatarUrl, badgeLabel: badgeLabel }), title: _jsx(ConversationInboxTitle, { peerType: item.peerType, displayName: displayName, hasUnread: unreadCount > 0 }) }));
39
+ if (hasLostGroupAccess) {
40
+ return threadCard;
41
+ }
42
+ return (_jsx(SwipeableRow, { actionsWidth: SWIPE_ACTION_WIDTH, actions: (closeSwipe) => (_jsx(ConversationMuteSwipeAction, { conversationID: item.conversationID, isMuted: isMuted, onClose: closeSwipe })), children: threadCard }));
30
43
  };
31
44
  export default ConversationInboxItem;
@@ -0,0 +1,8 @@
1
+ export interface ConversationMuteSwipeActionProps {
2
+ conversationID: string;
3
+ isMuted: boolean;
4
+ onClose: () => void;
5
+ }
6
+ declare const ConversationMuteSwipeAction: ({ conversationID, isMuted, onClose, }: ConversationMuteSwipeActionProps) => import("react/jsx-runtime").JSX.Element;
7
+ export default ConversationMuteSwipeAction;
8
+ //# sourceMappingURL=ConversationMuteSwipeAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationMuteSwipeAction.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/ConversationMuteSwipeAction.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,gCAAgC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,2BAA2B,GAAI,uCAIlC,gCAAgC,4CAwDlC,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { useTranslation } from "react-i18next";
5
+ import SwipeActionButton from "../../components/conversation/SwipeActionButton";
6
+ import { useMuteConversation } from "../../hooks/session/useMuteConversation";
7
+ import MuteConversationConfirmModal from "./MuteConversationConfirmModal";
8
+ const ConversationMuteSwipeAction = ({ conversationID, isMuted, onClose, }) => {
9
+ const { t } = useTranslation("conversation-inbox");
10
+ const { muteConversation, unmuteConversation, isPending } = useMuteConversation();
11
+ const [isConfirmOpen, setIsConfirmOpen] = useState(false);
12
+ const stopPropagation = (event) => {
13
+ event.stopPropagation();
14
+ };
15
+ const handleClick = () => {
16
+ onClose();
17
+ if (isMuted) {
18
+ unmuteConversation(conversationID);
19
+ return;
20
+ }
21
+ setIsConfirmOpen(true);
22
+ };
23
+ const handleConfirmMute = () => {
24
+ setIsConfirmOpen(false);
25
+ muteConversation(conversationID);
26
+ };
27
+ return (_jsxs(_Fragment, { children: [_jsx(SwipeActionButton, { testId: "conversation-inbox-mute-action", icon: isMuted ? "bell-b" : "bell-off-b", label: t(isMuted
28
+ ? "thread_card.mute.unmute_action"
29
+ : "thread_card.mute.mute_action"), onClick: handleClick, className: "bg-gray-400" }), _jsx("span", { role: "presentation", onPointerDown: stopPropagation, onPointerMove: stopPropagation, onPointerUp: stopPropagation, onClick: stopPropagation, children: _jsx(MuteConversationConfirmModal, { open: isConfirmOpen, loading: isPending, onCancel: () => setIsConfirmOpen(false), onConfirm: handleConfirmMute }) })] }));
30
+ };
31
+ export default ConversationMuteSwipeAction;
@@ -0,0 +1,9 @@
1
+ export interface MuteConversationConfirmModalProps {
2
+ open: boolean;
3
+ loading: boolean;
4
+ onCancel: () => void;
5
+ onConfirm: () => void;
6
+ }
7
+ declare const MuteConversationConfirmModal: ({ open, loading, onCancel, onConfirm, }: MuteConversationConfirmModalProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default MuteConversationConfirmModal;
9
+ //# sourceMappingURL=MuteConversationConfirmModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuteConversationConfirmModal.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/MuteConversationConfirmModal.tsx"],"names":[],"mappings":"AAMA,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,QAAA,MAAM,4BAA4B,GAAI,yCAKnC,iCAAiC,4CAmEnC,CAAC;AAEF,eAAe,4BAA4B,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Modal, Button } from "antd";
4
+ import { Trans, useTranslation } from "react-i18next";
5
+ import { images } from "../../constants/images";
6
+ const MuteConversationConfirmModal = ({ open, loading, onCancel, onConfirm, }) => {
7
+ const { t } = useTranslation("conversation-inbox");
8
+ return (_jsx(Modal, { open: open, onCancel: onCancel, footer: null, closable: false, centered: true, width: 400, maskClosable: !loading, keyboard: !loading, styles: {
9
+ content: { padding: 0, overflow: "hidden" },
10
+ body: { padding: 0 },
11
+ }, children: _jsxs("div", { role: "alertdialog", "aria-label": t("thread_card.mute.confirm_title"), className: "flex flex-col", children: [_jsx("div", { className: "border-b border-gray-25 p-4", children: _jsx("p", { className: "text-center text-[18px] font-bold leading-[1.6] text-gray-900", children: t("thread_card.mute.confirm_title") }) }), _jsx("div", { className: "flex items-center justify-center p-4", children: _jsx("img", { src: images.muteNotificationWarning, alt: "", "aria-hidden": "true", className: "h-[144px] w-[120px]" }) }), _jsx("p", { className: "px-4 pb-4 text-[16px] leading-[1.6] text-gray-900", children: _jsx(Trans, { t: t, i18nKey: "thread_card.mute.confirm_desc", components: { bold: _jsx("strong", { className: "font-bold" }) } }) }), _jsx("div", { className: "border-t border-gray-25" }), _jsxs("div", { className: "flex gap-2 p-4", children: [_jsx(Button, { className: "h-12 flex-1 !rounded-lg !border-gray-25 !bg-gray-25 !text-[16px] !font-medium !text-gray-900", onClick: onCancel, disabled: loading, children: t("thread_card.mute.confirm_secondary") }), _jsx(Button, { type: "primary", className: "h-12 flex-1 !rounded-lg !border-primary-400 !bg-primary-400 !text-[16px] !font-medium !text-white hover:!border-primary-300 hover:!bg-primary-300", onClick: onConfirm, loading: loading, children: t("thread_card.mute.confirm_primary") })] })] }) }));
12
+ };
13
+ export default MuteConversationConfirmModal;
@@ -7,4 +7,8 @@ export { default as ConversationInboxItem } from "./ConversationInboxItem";
7
7
  export type { ConversationInboxItemProps } from "./ConversationInboxItem";
8
8
  export { default as ConversationInboxList } from "./ConversationInboxList";
9
9
  export type { ConversationInboxListProps } from "./ConversationInboxList";
10
+ export { default as ConversationMuteSwipeAction } from "./ConversationMuteSwipeAction";
11
+ export type { ConversationMuteSwipeActionProps } from "./ConversationMuteSwipeAction";
12
+ export { default as MuteConversationConfirmModal } from "./MuteConversationConfirmModal";
13
+ export type { MuteConversationConfirmModalProps } from "./MuteConversationConfirmModal";
10
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAE/E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-inbox/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAE/E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAE5E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AAEtF,OAAO,EAAE,OAAO,IAAI,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AACzF,YAAY,EAAE,iCAAiC,EAAE,MAAM,gCAAgC,CAAC"}
@@ -3,3 +3,5 @@ export { default as ConversationPeerAvatar } from "./ConversationPeerAvatar";
3
3
  export { default as ConversationInboxTitle } from "./ConversationInboxTitle";
4
4
  export { default as ConversationInboxItem } from "./ConversationInboxItem";
5
5
  export { default as ConversationInboxList } from "./ConversationInboxList";
6
+ export { default as ConversationMuteSwipeAction } from "./ConversationMuteSwipeAction";
7
+ export { default as MuteConversationConfirmModal } from "./MuteConversationConfirmModal";
@@ -0,0 +1,9 @@
1
+ interface AddMemberModalProps {
2
+ open: boolean;
3
+ groupID: string;
4
+ existingMemberIDs: string[];
5
+ onClose: () => void;
6
+ }
7
+ declare const AddMemberModal: ({ open, groupID, existingMemberIDs, onClose, }: AddMemberModalProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default AddMemberModal;
9
+ //# sourceMappingURL=AddMemberModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddMemberModal.d.ts","sourceRoot":"","sources":["../../../src/components/group/AddMemberModal.tsx"],"names":[],"mappings":"AAYA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,cAAc,GAAI,gDAKrB,mBAAmB,4CA2LrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,70 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useDebounce } from "ahooks";
4
+ import { Avatar, Button, Empty, Input, Modal, Spin, message } from "antd";
5
+ import clsx from "clsx";
6
+ import { useState } from "react";
7
+ import { useTranslation } from "react-i18next";
8
+ import { Icon } from "../../components/icon";
9
+ import { useEligibleAccountsForGroup } from "../../hooks/group/useEligibleAccountsForGroup";
10
+ import { useAddGroupMember } from "../../hooks/group/useAddGroupMember";
11
+ const AddMemberModal = ({ open, groupID, existingMemberIDs, onClose, }) => {
12
+ const { t } = useTranslation("conversation-inbox");
13
+ const { t: tCommon } = useTranslation();
14
+ const [search, setSearch] = useState("");
15
+ const [selectedMembers, setSelectedMembers] = useState([]);
16
+ const debouncedSearch = useDebounce(search, { wait: 300 });
17
+ const { accounts, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = useEligibleAccountsForGroup(debouncedSearch, existingMemberIDs, open);
18
+ const { mutateAsync: addGroupMember, isPending: isSubmitting } = useAddGroupMember();
19
+ const selectedIds = new Set(selectedMembers.map((member) => member.userID));
20
+ const toggleMember = (account) => {
21
+ if (selectedIds.has(account.userID)) {
22
+ setSelectedMembers((prev) => prev.filter((member) => member.userID !== account.userID));
23
+ return;
24
+ }
25
+ setSelectedMembers((prev) => [...prev, account]);
26
+ };
27
+ const handleRemoveMember = (userID) => {
28
+ setSelectedMembers((prev) => prev.filter((member) => member.userID !== userID));
29
+ };
30
+ const handleClearAll = () => {
31
+ setSelectedMembers([]);
32
+ };
33
+ const handleClose = () => {
34
+ setSearch("");
35
+ setSelectedMembers([]);
36
+ onClose();
37
+ };
38
+ const handleListScroll = (event) => {
39
+ if (!hasNextPage || isFetchingNextPage)
40
+ return;
41
+ const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
42
+ if (scrollHeight - scrollTop - clientHeight < 80) {
43
+ void fetchNextPage();
44
+ }
45
+ };
46
+ const handleSubmit = async () => {
47
+ var _a, _b;
48
+ if (!groupID || selectedMembers.length === 0)
49
+ return;
50
+ try {
51
+ await addGroupMember({
52
+ groupID,
53
+ userIDList: selectedMembers.map((member) => member.userID),
54
+ });
55
+ message.success(t("add_member.add_member_success"));
56
+ handleClose();
57
+ }
58
+ catch (error) {
59
+ const errorMessage = ((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || t("add_member.add_member_failed");
60
+ message.error(errorMessage);
61
+ }
62
+ };
63
+ return (_jsx(Modal, { centered: true, open: open, onCancel: handleClose, footer: null, title: t("add_member.title"), getContainer: false, children: _jsxs("div", { className: "flex flex-col gap-3 pt-2", children: [_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("create_group.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }) }), _jsxs("div", { className: "flex items-center justify-between px-1", children: [_jsx("span", { className: "text-sm text-gray-500", children: t("create_group.selected_count", { count: selectedMembers.length }) }), selectedMembers.length > 0 && (_jsx("button", { type: "button", className: "text-sm text-primary-400 hover:text-primary-500 hover:underline", onClick: handleClearAll, children: t("create_group.clear_all") }))] }), selectedMembers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedMembers.map((member) => (_jsxs("span", { className: "flex items-center gap-1.5 rounded-full bg-gray-100 pl-1 pr-2 py-1", children: [_jsx(Avatar, { src: member.avatar, size: 20, className: "shrink-0", children: member.displayName.charAt(0) || "A" }), _jsx("span", { className: "max-w-[120px] truncate text-sm text-gray-900", children: member.displayName }), _jsx(Icon, { icon: "close-b", size: 12, className: "shrink-0 cursor-pointer text-gray-400", onClick: () => handleRemoveMember(member.userID) })] }, member.userID))) })), _jsx("div", { className: "max-h-72 overflow-y-auto", onScroll: handleListScroll, children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("create_group.no_member_found") })) : (_jsxs(_Fragment, { children: [accounts.map((account) => {
64
+ const isSelected = selectedIds.has(account.userID);
65
+ return (_jsxs("div", { className: "flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 transition-colors duration-150 hover:bg-[#f5f5f5]", onClick: () => toggleMember(account), children: [_jsx(Avatar, { src: account.avatar, size: 40, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), _jsx("span", { className: clsx("flex h-4 w-4 shrink-0 items-center justify-center rounded border", isSelected
66
+ ? "border-primary-400 bg-primary-400"
67
+ : "border-gray-300 bg-white"), children: isSelected && (_jsx(Icon, { icon: "check-b", size: 11, className: "text-white" })) })] }, account.userID));
68
+ }), isFetchingNextPage && (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) }))] })) }), _jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [_jsx(Button, { onClick: handleClose, children: tCommon("cancel") }), _jsx(Button, { type: "primary", disabled: selectedMembers.length === 0, loading: isSubmitting, onClick: handleSubmit, children: t("add_member.confirm") })] })] }) }));
69
+ };
70
+ export default AddMemberModal;
@@ -0,0 +1,9 @@
1
+ interface AdminPermissionModalProps {
2
+ open: boolean;
3
+ groupID: string;
4
+ onClose: () => void;
5
+ inline?: boolean;
6
+ }
7
+ declare const AdminPermissionModal: ({ open, groupID, onClose, inline, }: AdminPermissionModalProps) => import("react/jsx-runtime").JSX.Element | null;
8
+ export default AdminPermissionModal;
9
+ //# sourceMappingURL=AdminPermissionModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminPermissionModal.d.ts","sourceRoot":"","sources":["../../../src/components/group/AdminPermissionModal.tsx"],"names":[],"mappings":"AAYA,UAAU,yBAAyB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IAIpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAMD,QAAA,MAAM,oBAAoB,GAAI,qCAK3B,yBAAyB,mDA0J3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { ConfigProvider, Modal, Switch, message } from "antd";
5
+ import { useTranslation } from "react-i18next";
6
+ import { useGroupPermission } from "../../hooks/group/useGroupPermission";
7
+ import { Icon } from "../../components/icon";
8
+ import { PERMISSION_SWITCH_COLOR } from "../../constants";
9
+ import useGroupPermissionOverridesStore from "../../store/groupPermissionOverrides";
10
+ // Mocked config UI (DROPPII-30248 follow-up, "Configure Admin Permissions")
11
+ // — applies to the whole Admin role in the group, not a per-member override
12
+ // (confirmed with PO). No BE persistence yet, committed to the in-memory
13
+ // override store on "Lưu" only.
14
+ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, }) => {
15
+ const { t } = useTranslation();
16
+ const permissions = useGroupPermission(groupID);
17
+ const setAdminOverrides = useGroupPermissionOverridesStore((state) => state.setAdminOverrides);
18
+ const [isSaving, setIsSaving] = useState(false);
19
+ const [pending, setPending] = useState({
20
+ sendMessage: permissions.sendMessage,
21
+ pinMessage: permissions.pinMessage,
22
+ viewMemberInformation: permissions.viewMemberInformation,
23
+ addMember: permissions.addMember,
24
+ removeMember: permissions.removeMember,
25
+ updateGroupInfo: permissions.updateGroupName,
26
+ });
27
+ useEffect(() => {
28
+ if (!open)
29
+ return;
30
+ setPending({
31
+ sendMessage: permissions.sendMessage,
32
+ pinMessage: permissions.pinMessage,
33
+ viewMemberInformation: permissions.viewMemberInformation,
34
+ addMember: permissions.addMember,
35
+ removeMember: permissions.removeMember,
36
+ updateGroupInfo: permissions.updateGroupName,
37
+ });
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ }, [open]);
40
+ const hasChanges = pending.sendMessage !== permissions.sendMessage ||
41
+ pending.pinMessage !== permissions.pinMessage ||
42
+ pending.viewMemberInformation !== permissions.viewMemberInformation ||
43
+ pending.addMember !== permissions.addMember ||
44
+ pending.removeMember !== permissions.removeMember ||
45
+ pending.updateGroupInfo !== permissions.updateGroupName;
46
+ const handleSave = async () => {
47
+ if (!groupID)
48
+ return;
49
+ setIsSaving(true);
50
+ setAdminOverrides(groupID, pending);
51
+ message.success(t("update_admin_permissions_success"));
52
+ setIsSaving(false);
53
+ onClose();
54
+ };
55
+ const rows = (_jsx(ConfigProvider, { theme: { token: { colorPrimary: PERMISSION_SWITCH_COLOR } }, children: _jsxs("div", { className: "flex flex-col py-2", children: [_jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_send_message") }), _jsx(Switch, { checked: pending.sendMessage, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { sendMessage: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_pin_unpin_message") }), _jsx(Switch, { checked: pending.pinMessage, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { pinMessage: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_view_member_info") }), _jsx(Switch, { checked: pending.viewMemberInformation, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { viewMemberInformation: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_add_member") }), _jsx(Switch, { checked: pending.addMember, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { addMember: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_remove_member") }), _jsx(Switch, { checked: pending.removeMember, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { removeMember: checked }))) })] }), _jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [_jsx("span", { className: "text-sm", children: t("permission_update_group_info") }), _jsx(Switch, { checked: pending.updateGroupInfo, onChange: (checked) => setPending((prev) => (Object.assign(Object.assign({}, prev), { updateGroupInfo: checked }))) })] })] }) }));
56
+ if (inline) {
57
+ if (!open)
58
+ return null;
59
+ return (_jsxs("div", { className: "absolute inset-0 bg-white flex flex-col z-10", children: [_jsxs("div", { className: "flex w-full items-center border-b border-gray-100 p-2", children: [_jsx("button", { type: "button", className: "flex shrink-0 cursor-pointer items-center justify-center p-2 text-primary-400", onClick: onClose, children: _jsx(Icon, { icon: "arrow-left-o", size: 24 }) }), _jsx("p", { className: "min-w-0 flex-1 truncate px-2 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("admin_permission_settings") })] }), _jsxs("div", { className: "flex-1 overflow-y-auto", children: [rows, _jsxs("div", { className: "flex items-center justify-end gap-2 px-4 py-3 border-t border-gray-100", children: [_jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full border border-gray-300 text-gray-700 text-sm font-medium", onClick: onClose, children: t("cancel") }), _jsx("button", { type: "button", className: "px-4 py-1.5 rounded-full bg-primary-400 text-white text-sm font-medium disabled:bg-gray-200 disabled:text-gray-400", disabled: !hasChanges || isSaving, onClick: handleSave, children: t("save") })] })] })] }));
60
+ }
61
+ return (_jsx(Modal, { centered: true, open: open, onOk: handleSave, onCancel: onClose, title: t("admin_permission_settings"), okText: t("save"), cancelText: t("cancel"), confirmLoading: isSaving, okButtonProps: { disabled: !hasChanges }, getContainer: false, children: _jsx("div", { className: "mt-2", children: rows }) }));
62
+ };
63
+ export default AdminPermissionModal;
@@ -1,7 +1,6 @@
1
1
  interface CreateGroupFormProps {
2
- onBack: () => void;
3
2
  onDone: () => void;
4
3
  }
5
- declare const CreateGroupForm: ({ onBack, onDone }: CreateGroupFormProps) => import("react/jsx-runtime").JSX.Element;
4
+ declare const CreateGroupForm: ({ onDone }: CreateGroupFormProps) => import("react/jsx-runtime").JSX.Element;
6
5
  export default CreateGroupForm;
7
6
  //# sourceMappingURL=CreateGroupForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateGroupForm.d.ts","sourceRoot":"","sources":["../../../src/components/group/CreateGroupForm.tsx"],"names":[],"mappings":"AAcA,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,oBAAoB,oBAAoB,4CA8KhE,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"CreateGroupForm.d.ts","sourceRoot":"","sources":["../../../src/components/group/CreateGroupForm.tsx"],"names":[],"mappings":"AAcA,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,YAAY,oBAAoB,4CAqKxD,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -8,7 +8,7 @@ import { Icon } from "../../components/icon";
8
8
  import { useCreateGroup } from "../../hooks/group/useCreateGroup";
9
9
  import GroupMemberPicker from "./GroupMemberPicker";
10
10
  const GROUP_NAME_MAX_LENGTH = 255;
11
- const CreateGroupForm = ({ onBack, onDone }) => {
11
+ const CreateGroupForm = ({ onDone }) => {
12
12
  const { t } = useTranslation("conversation-inbox");
13
13
  const { createGroup } = useCreateGroup();
14
14
  const [groupName, setGroupName] = useState("");
@@ -63,12 +63,12 @@ const CreateGroupForm = ({ onBack, onDone }) => {
63
63
  description: t("create_group.privacy_public_desc"),
64
64
  },
65
65
  ], [t]);
66
- return (_jsxs("div", { className: "flex max-h-[70vh] flex-col", children: [_jsxs("button", { type: "button", onClick: onBack, className: "mb-3 flex w-fit items-center gap-1 text-sm text-gray-500 hover:text-gray-700", children: [_jsx(Icon, { icon: "angle-left-o", size: 16 }), t("create_group.back")] }), _jsxs("div", { className: "flex flex-col overflow-y-auto pr-1", children: [_jsxs("div", { className: "flex items-center gap-4 pb-6", children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg", showUploadList: false, beforeUpload: handleAvatarSelect, children: _jsxs("div", { className: "relative flex h-20 w-20 shrink-0 cursor-pointer items-center justify-center rounded-2xl bg-primary-50", children: [avatarPreviewUrl ? (
66
+ return (_jsxs("div", { className: "flex max-h-[70vh] flex-col", children: [_jsxs("div", { className: "flex flex-col overflow-y-auto pr-1", children: [_jsxs("div", { className: "flex items-center gap-4 py-6", children: [_jsx(Upload, { accept: "image/jpeg, image/png, image/jpg", showUploadList: false, beforeUpload: handleAvatarSelect, children: _jsxs("div", { className: "relative flex h-20 w-20 shrink-0 cursor-pointer items-center justify-center rounded-2xl bg-primary-50", children: [avatarPreviewUrl ? (
67
67
  // eslint-disable-next-line @next/next/no-img-element
68
- _jsx("img", { src: avatarPreviewUrl, alt: "", className: "h-full w-full rounded-2xl object-cover" })) : (_jsx(Icon, { icon: "user-three-b", size: 32, className: "text-primary-400" })), _jsx("span", { className: "absolute -bottom-1 -right-1 flex h-7 w-7 items-center justify-center rounded-full bg-gray-500", children: _jsx(Icon, { icon: "camera-b", size: 14, className: "text-white" }) })] }) }), _jsx(Input, { size: "large", value: groupName, onChange: (event) => setGroupName(event.target.value.slice(0, GROUP_NAME_MAX_LENGTH)), maxLength: GROUP_NAME_MAX_LENGTH, placeholder: t("create_group.name_placeholder"), className: "flex-1 text-base" })] }), _jsxs("div", { className: "flex flex-col gap-3 border-t border-gray-100 py-6", children: [_jsx("span", { className: "text-base font-semibold text-gray-900", children: t("create_group.privacy_label") }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: privacyOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => setIsPublic(option.value), className: clsx("rounded-xl border-2 p-4 text-left transition-colors", isPublic === option.value
68
+ _jsx("img", { src: avatarPreviewUrl, alt: "", className: "h-full w-full rounded-2xl object-cover" })) : (_jsx(Icon, { icon: "user-three-b", size: 32, className: "text-primary-400" })), _jsx("span", { className: "absolute -bottom-1 -right-1 flex h-7 w-7 items-center justify-center rounded-full bg-gray-500", children: _jsx(Icon, { icon: "camera-b", size: 14, className: "text-white" }) })] }) }), _jsx(Input, { size: "large", value: groupName, onChange: (event) => setGroupName(event.target.value.slice(0, GROUP_NAME_MAX_LENGTH)), maxLength: GROUP_NAME_MAX_LENGTH, placeholder: t("create_group.name_placeholder"), className: "flex-1 text-base" })] }), _jsxs("div", { className: "flex flex-col gap-3 border-t border-gray-100 py-6", children: [_jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-gray-400", children: t("create_group.privacy_label") }), _jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: privacyOptions.map((option) => (_jsxs("button", { type: "button", onClick: () => setIsPublic(option.value), className: clsx("rounded-xl border-2 p-4 text-left transition-colors", isPublic === option.value
69
69
  ? "border-primary-400 bg-primary-50"
70
70
  : "border-gray-200 bg-white"), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { icon: option.icon, size: 18, className: isPublic === option.value
71
71
  ? "text-primary-400"
72
- : "text-gray-400" }), _jsx("span", { className: "text-base font-semibold text-gray-900", children: option.label })] }), _jsx("p", { className: "mt-1.5 text-sm text-gray-500", children: option.description })] }, String(option.value)))) })] }), _jsx("div", { className: "border-t border-gray-100 pt-6", children: _jsx(GroupMemberPicker, { selectedMembers: selectedMembers, onChangeSelectedMembers: setSelectedMembers }) })] }), _jsxs("div", { className: "flex shrink-0 justify-end gap-2 pt-4", children: [_jsx(Button, { onClick: onDone, children: t("create_group.cancel") }), _jsx(Button, { type: "primary", disabled: !canSubmit, loading: isSubmitting, onClick: handleSubmit, children: t("create_group.submit") })] })] }));
72
+ : "text-gray-400" }), _jsx("span", { className: "text-base font-semibold text-gray-900", children: option.label })] }), _jsx("p", { className: "mt-2 text-sm text-gray-500", children: option.description })] }, String(option.value)))) })] }), _jsx("div", { className: "border-t border-gray-100 py-6", children: _jsx(GroupMemberPicker, { selectedMembers: selectedMembers, onChangeSelectedMembers: setSelectedMembers }) })] }), _jsxs("div", { className: "flex shrink-0 justify-end gap-2 pt-4", children: [_jsx(Button, { onClick: onDone, children: t("create_group.cancel") }), _jsx(Button, { type: "primary", disabled: !canSubmit, loading: isSubmitting, onClick: handleSubmit, children: t("create_group.submit") })] })] }));
73
73
  };
74
74
  export default CreateGroupForm;
@@ -1 +1 @@
1
- {"version":3,"file":"GroupMemberPicker.d.ts","sourceRoot":"","sources":["../../../src/components/group/GroupMemberPicker.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,UAAU,sBAAsB;IAC9B,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,uBAAuB,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,iBAAiB,GAAI,+CAGxB,sBAAsB,4CAoIxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"GroupMemberPicker.d.ts","sourceRoot":"","sources":["../../../src/components/group/GroupMemberPicker.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,UAAU,sBAAsB;IAC9B,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,uBAAuB,EAAE,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,IAAI,CAAC;CACnE;AAED,QAAA,MAAM,iBAAiB,GAAI,+CAGxB,sBAAsB,4CAoJxB,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useDebounce } from "ahooks";
4
4
  import { Avatar, Empty, Input, Spin } from "antd";
5
5
  import clsx from "clsx";
@@ -11,7 +11,15 @@ const GroupMemberPicker = ({ selectedMembers, onChangeSelectedMembers, }) => {
11
11
  const { t } = useTranslation("conversation-inbox");
12
12
  const [search, setSearch] = useState("");
13
13
  const debouncedSearch = useDebounce(search, { wait: 300 });
14
- const { accounts, isLoading } = useEligibleAccounts(debouncedSearch);
14
+ const { accounts, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = useEligibleAccounts(debouncedSearch);
15
+ const handleListScroll = (event) => {
16
+ if (!hasNextPage || isFetchingNextPage)
17
+ return;
18
+ const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;
19
+ if (scrollHeight - scrollTop - clientHeight < 80) {
20
+ void fetchNextPage();
21
+ }
22
+ };
15
23
  const selectedIds = new Set(selectedMembers.map((member) => member.userID));
16
24
  const toggleMember = (account) => {
17
25
  if (selectedIds.has(account.userID)) {
@@ -26,11 +34,11 @@ const GroupMemberPicker = ({ selectedMembers, onChangeSelectedMembers, }) => {
26
34
  const handleClearAll = () => {
27
35
  onChangeSelectedMembers([]);
28
36
  };
29
- return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("create_group.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }) }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("create_group.no_member_found") })) : (accounts.map((account) => {
30
- const isSelected = selectedIds.has(account.userID);
31
- return (_jsxs("div", { className: clsx("flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 transition-colors", isSelected ? "bg-primary-50" : "hover:bg-gray-50"), onClick: () => toggleMember(account), children: [_jsx(Avatar, { src: account.avatar, size: 40, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), _jsx("span", { className: clsx("flex h-4 w-4 shrink-0 items-center justify-center rounded border", isSelected
32
- ? "border-primary-400 bg-primary-400"
33
- : "border-gray-300 bg-white"), children: isSelected && (_jsx(Icon, { icon: "check-b", size: 11, className: "text-white" })) })] }, account.userID));
34
- })) })] }), _jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsxs("div", { className: "flex items-center justify-between px-1", children: [_jsx("span", { className: "text-sm text-gray-500", children: t("create_group.selected_count", { count: selectedMembers.length }) }), selectedMembers.length > 0 && (_jsx("button", { type: "button", className: "text-sm text-primary-400 hover:text-primary-500 hover:underline", onClick: handleClearAll, children: t("create_group.clear_all") }))] }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: selectedMembers.length === 0 ? (_jsxs("div", { className: "flex h-full min-h-[200px] flex-col items-center justify-center gap-2 text-center", children: [_jsx(Icon, { icon: "user-three-b", size: 28, className: "text-gray-300" }), _jsx("span", { className: "text-sm text-gray-400", children: t("create_group.no_member_selected") })] })) : (selectedMembers.map((member) => (_jsxs("div", { className: "flex items-center gap-3 rounded-lg px-3 py-3", children: [_jsx(Avatar, { src: member.avatar, size: 40, className: "shrink-0", children: member.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: member.displayName }), _jsx(Icon, { icon: "close-b", size: 14, className: "shrink-0 cursor-pointer text-gray-400", onClick: () => handleRemoveMember(member.userID) })] }, member.userID)))) })] })] }));
37
+ return (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: t("create_group.search_member_placeholder"), prefix: _jsx(Icon, { icon: "search-o", size: 16, className: "text-gray-400" }) }), _jsx("div", { className: "max-h-64 overflow-y-auto", onScroll: handleListScroll, children: isLoading ? (_jsx("div", { className: "flex items-center justify-center py-6", children: _jsx(Spin, {}) })) : accounts.length === 0 ? (_jsx(Empty, { description: t("create_group.no_member_found") })) : (_jsxs(_Fragment, { children: [accounts.map((account) => {
38
+ const isSelected = selectedIds.has(account.userID);
39
+ return (_jsxs("div", { className: "flex cursor-pointer items-center gap-3 rounded-lg px-3 py-3 transition-colors duration-150 hover:bg-[#f5f5f5]", onClick: () => toggleMember(account), children: [_jsx(Avatar, { src: account.avatar, size: 40, className: "shrink-0", children: account.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: account.displayName }), _jsx("span", { className: clsx("flex h-4 w-4 shrink-0 items-center justify-center rounded border", isSelected
40
+ ? "border-primary-400 bg-primary-400"
41
+ : "border-gray-300 bg-white"), children: isSelected && (_jsx(Icon, { icon: "check-b", size: 11, className: "text-white" })) })] }, account.userID));
42
+ }), isFetchingNextPage && (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx(Spin, { size: "small" }) }))] })) })] }), _jsxs("div", { className: "flex flex-col gap-2 rounded-xl border border-gray-200 p-3", children: [_jsxs("div", { className: "flex items-center justify-between px-1", children: [_jsx("span", { className: "text-sm text-gray-500", children: t("create_group.selected_count", { count: selectedMembers.length }) }), selectedMembers.length > 0 && (_jsx("button", { type: "button", className: "text-sm text-primary-400 hover:text-primary-500 hover:underline", onClick: handleClearAll, children: t("create_group.clear_all") }))] }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: selectedMembers.length === 0 ? (_jsxs("div", { className: "flex h-full min-h-[200px] flex-col items-center justify-center gap-2 text-center", children: [_jsx(Icon, { icon: "user-three-b", size: 28, className: "text-gray-300" }), _jsx("span", { className: "text-sm text-gray-400", children: t("create_group.no_member_selected") })] })) : (selectedMembers.map((member) => (_jsxs("div", { className: "flex items-center gap-3 rounded-lg px-3 py-3", children: [_jsx(Avatar, { src: member.avatar, size: 40, className: "shrink-0", children: member.displayName.charAt(0) || "A" }), _jsx("span", { className: "flex-1 truncate text-base font-medium text-gray-900", children: member.displayName }), _jsx(Icon, { icon: "close-b", size: 14, className: "shrink-0 cursor-pointer text-gray-400", onClick: () => handleRemoveMember(member.userID) })] }, member.userID)))) })] })] }));
35
43
  };
36
44
  export default GroupMemberPicker;