@droppii-org/chat-sdk 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
  2. package/dist/components/conversation/ConversationBySessionItem.js +3 -1
  3. package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
  4. package/dist/components/conversation/DeskConversationList.js +1 -1
  5. package/dist/components/conversation-detail/PinnedMessagesPanel.d.ts.map +1 -1
  6. package/dist/components/conversation-detail/PinnedMessagesPanel.js +1 -1
  7. package/dist/components/conversation-detail/ThreadProfilePanel.d.ts.map +1 -1
  8. package/dist/components/conversation-detail/ThreadProfilePanel.js +1 -1
  9. package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
  10. package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
  11. package/dist/components/group/AddMemberModal.d.ts.map +1 -1
  12. package/dist/components/group/AddMemberModal.js +1 -1
  13. package/dist/components/group/AdminPermissionModal.d.ts +7 -1
  14. package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
  15. package/dist/components/group/AdminPermissionModal.js +88 -33
  16. package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
  17. package/dist/components/group/CreateGroupForm.js +44 -10
  18. package/dist/components/group/GroupCompositeAvatar.d.ts.map +1 -1
  19. package/dist/components/group/GroupCompositeAvatar.js +19 -7
  20. package/dist/components/media-collection/index.d.ts.map +1 -1
  21. package/dist/components/media-collection/index.js +3 -3
  22. package/dist/components/media-resources/MediaResourcePanel.d.ts.map +1 -1
  23. package/dist/components/media-resources/MediaResourcePanel.js +1 -1
  24. package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
  25. package/dist/components/message/GroupMembersDrawer.js +57 -29
  26. package/dist/components/message/MessageHeader.d.ts.map +1 -1
  27. package/dist/components/message/MessageHeader.js +16 -5
  28. package/dist/components/message/item/GroupNotificationMessage.d.ts +7 -0
  29. package/dist/components/message/item/GroupNotificationMessage.d.ts.map +1 -0
  30. package/dist/components/message/item/GroupNotificationMessage.js +13 -0
  31. package/dist/components/message/item/index.d.ts.map +1 -1
  32. package/dist/components/message/item/index.js +10 -2
  33. package/dist/components/message/item/renderMessageContent.d.ts.map +1 -1
  34. package/dist/components/message/item/renderMessageContent.js +18 -0
  35. package/dist/components/search-conversation/SearchDrawer.d.ts.map +1 -1
  36. package/dist/components/search-conversation/SearchDrawer.js +1 -1
  37. package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
  38. package/dist/components/thread/GroupThreadInfo.js +33 -3
  39. package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -1
  40. package/dist/components/thread/SingleThreadInfo.js +1 -1
  41. package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
  42. package/dist/components/thread/ThreadInfo.js +2 -1
  43. package/dist/components/video-player/VideoPlayerModal.d.ts.map +1 -1
  44. package/dist/components/video-player/VideoPlayerModal.js +1 -1
  45. package/dist/hooks/common/useUploadFile.d.ts +3 -0
  46. package/dist/hooks/common/useUploadFile.d.ts.map +1 -0
  47. package/dist/hooks/common/useUploadFile.js +15 -0
  48. package/dist/hooks/group/useGroupMembers.d.ts +3 -1
  49. package/dist/hooks/group/useGroupMembers.d.ts.map +1 -1
  50. package/dist/hooks/group/useGroupMembers.js +9 -0
  51. package/dist/hooks/message/useMessage.d.ts.map +1 -1
  52. package/dist/hooks/message/useMessage.js +2 -1
  53. package/dist/locales/vi/common.json +1 -0
  54. package/dist/locales/vi/conversation-inbox.json +4 -2
  55. package/dist/services/routes.d.ts +3 -0
  56. package/dist/services/routes.d.ts.map +1 -1
  57. package/dist/services/routes.js +3 -0
  58. package/dist/store/conversation.d.ts.map +1 -1
  59. package/dist/store/conversation.js +10 -0
  60. package/dist/store/session.d.ts +13 -1
  61. package/dist/store/session.d.ts.map +1 -1
  62. package/dist/store/session.js +6 -2
  63. package/dist/types/dto.d.ts +11 -0
  64. package/dist/types/dto.d.ts.map +1 -1
  65. package/dist/utils/imCommon.d.ts +4 -0
  66. package/dist/utils/imCommon.d.ts.map +1 -1
  67. package/dist/utils/imCommon.js +16 -0
  68. package/package.json +1 -1
@@ -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;AAShD,UAAU,8BAA8B;IACtC,WAAW,EAAE,gBAAgB,CAAC;IAC9B,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,yBAAyB,GAAI,iCAGhC,8BAA8B,mDAoEhC,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;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"}
@@ -7,6 +7,8 @@ import { Avatar } from "antd";
7
7
  import { useChatContext } from "../../context/ChatContext";
8
8
  import { useConversationDisplayData } from "../../hooks/conversation/useConversation";
9
9
  import { formatTimestamp, parseLatestMessage } from "../../utils/common";
10
+ import { isGroupSession } from "../../utils/imCommon";
11
+ import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
10
12
  import ThreadCard from "./ThreadCard";
11
13
  const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
12
14
  var _a;
@@ -43,6 +45,6 @@ const ConversationBySessionItem = ({ sessionItem, latestPreview, }) => {
43
45
  showName: conversation.showName,
44
46
  }), time: latestMsgSendTime > 0
45
47
  ? formatTimestamp(latestMsgSendTime, { hasTime: true })
46
- : undefined, avatar: _jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" }), title: _jsx("h3", { className: "truncate text-sm font-semibold text-gray-900", children: displayName }) }));
48
+ : undefined, avatar: isGroupSession(conversation.conversationType) ? (_jsx(GroupCompositeAvatar, { groupID: conversation.groupID, faceURL: avatar, groupName: displayName, size: 48 })) : (_jsx(Avatar, { size: 48, src: avatar, children: ((_a = displayName === null || displayName === void 0 ? void 0 : displayName.charAt) === null || _a === void 0 ? void 0 : _a.call(displayName, 0)) || "A" })), title: _jsx("h3", { className: "truncate text-sm font-semibold text-gray-900", children: displayName }) }));
47
49
  };
48
50
  export default ConversationBySessionItem;
@@ -1 +1 @@
1
- {"version":3,"file":"DeskConversationList.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/DeskConversationList.tsx"],"names":[],"mappings":"AA4BA,QAAA,MAAM,oBAAoB,+CA+RzB,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,+CAgSzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -109,6 +109,6 @@ const DeskConversationList = () => {
109
109
  body: {
110
110
  padding: 0,
111
111
  },
112
- }, getContainer: false, width: "100%", children: _jsx(SearchConversation, { searchTerm: debouncedSearch }) })] }), _jsx(ComposeNewMessageModal, { open: isComposeOpen, onClose: () => setIsComposeOpen(false) })] }));
112
+ }, getContainer: false, forceRender: true, width: "100%", children: _jsx(SearchConversation, { searchTerm: debouncedSearch }) })] }), _jsx(ComposeNewMessageModal, { open: isComposeOpen, onClose: () => setIsComposeOpen(false) })] }));
113
113
  };
114
114
  export default DeskConversationList;
@@ -1 +1 @@
1
- {"version":3,"file":"PinnedMessagesPanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/PinnedMessagesPanel.tsx"],"names":[],"mappings":"AAYA,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,mBAAmB,GAAI,mBAAmB,wBAAwB,4CAkGvE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"PinnedMessagesPanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/PinnedMessagesPanel.tsx"],"names":[],"mappings":"AAYA,UAAU,wBAAwB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,mBAAmB,GAAI,mBAAmB,wBAAwB,4CAmGvE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -46,6 +46,6 @@ const PinnedMessagesPanel = ({ open, onClose }) => {
46
46
  borderTopRightRadius: 16,
47
47
  overflow: "hidden",
48
48
  },
49
- }, getContainer: false, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-lg font-bold text-gray-900", children: t("pinned_panel_title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("pinned_panel_title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto py-3", children: [_jsx("p", { className: "px-3 text-sm leading-[160%] text-gray-600", children: t("pinned_panel_hint") }), pinnedMessageList.map((message) => (_jsx(PinnedMessageItem, { message: message, contextMenuOpen: openContextMenuId === message.clientMsgID, onContextMenuOpenChange: (isOpen) => setOpenContextMenuId(isOpen ? message.clientMsgID : null), onUnpin: handleUnpin, onJump: handleJump }, message.clientMsgID)))] })] }) }));
49
+ }, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-lg font-bold text-gray-900", children: t("pinned_panel_title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("pinned_panel_title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto py-3", children: [_jsx("p", { className: "px-3 text-sm leading-[160%] text-gray-600", children: t("pinned_panel_hint") }), pinnedMessageList.map((message) => (_jsx(PinnedMessageItem, { message: message, contextMenuOpen: openContextMenuId === message.clientMsgID, onContextMenuOpenChange: (isOpen) => setOpenContextMenuId(isOpen ? message.clientMsgID : null), onUnpin: handleUnpin, onJump: handleJump }, message.clientMsgID)))] })] }) }));
50
50
  };
51
51
  export default PinnedMessagesPanel;
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAW7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACzD;AAED,QAAA,MAAM,kBAAkB,GAAI,oDAKzB,uBAAuB,4CAiEzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ThreadProfilePanel.d.ts","sourceRoot":"","sources":["../../../src/components/conversation-detail/ThreadProfilePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAW7E,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACzD;AAED,QAAA,MAAM,kBAAkB,GAAI,oDAKzB,uBAAuB,4CAkEzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -16,6 +16,6 @@ const ThreadProfilePanel = ({ open, conversation, onClose, onSelectResource, })
16
16
  const badgeLabel = resolvePeerBadgeLabel(peerType, t);
17
17
  return (_jsx(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", styles: {
18
18
  body: { padding: 0 },
19
- }, getContainer: false, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-gray-25", children: [_jsxs("div", { className: "flex w-full items-center bg-white p-2", children: [_jsx("button", { type: "button", "data-testid": "thread-profile-back", 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("thread_detail.options_title") })] }), _jsxs("div", { className: "flex flex-col items-center gap-2 bg-white px-0 py-2", children: [_jsxs("div", { className: "relative h-24 w-24 shrink-0", children: [_jsx(Avatar, { size: PROFILE_AVATAR_SIZE, src: avatarUrl || undefined, alt: displayName, className: "uppercase", children: (displayName === null || displayName === void 0 ? void 0 : displayName.charAt(0)) || "A" }), badgeLabel && (_jsx("span", { className: clsx("absolute bottom-1 right-0 flex h-[17px] items-center justify-center rounded-md border border-white px-1 text-[10px] font-semibold leading-3 text-white shadow-badge-inset", resolvePeerBadgeColor(peerType)), children: badgeLabel }))] }), _jsx("p", { className: "w-full truncate text-center text-[18px] font-bold leading-[1.6] text-gray-900", children: displayName })] }), _jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx(MediaEntryCard, { onSelect: onSelectResource }) })] }) }));
19
+ }, getContainer: false, forceRender: true, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-gray-25", children: [_jsxs("div", { className: "flex w-full items-center bg-white p-2", children: [_jsx("button", { type: "button", "data-testid": "thread-profile-back", 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("thread_detail.options_title") })] }), _jsxs("div", { className: "flex flex-col items-center gap-2 bg-white px-0 py-2", children: [_jsxs("div", { className: "relative h-24 w-24 shrink-0", children: [_jsx(Avatar, { size: PROFILE_AVATAR_SIZE, src: avatarUrl || undefined, alt: displayName, className: "uppercase", children: (displayName === null || displayName === void 0 ? void 0 : displayName.charAt(0)) || "A" }), badgeLabel && (_jsx("span", { className: clsx("absolute bottom-1 right-0 flex h-[17px] items-center justify-center rounded-md border border-white px-1 text-[10px] font-semibold leading-3 text-white shadow-badge-inset", resolvePeerBadgeColor(peerType)), children: badgeLabel }))] }), _jsx("p", { className: "w-full truncate text-center text-[18px] font-bold leading-[1.6] text-gray-900", children: displayName })] }), _jsx("div", { className: "flex flex-col px-3 py-1.5", children: _jsx(MediaEntryCard, { onSelect: onSelectResource }) })] }) }));
20
20
  };
21
21
  export default ThreadProfilePanel;
@@ -1 +1 @@
1
- {"version":3,"file":"BotMenuSheet.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotMenuSheet.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CAC3C;AAED,QAAA,MAAM,YAAY,GAAI,+CAMnB,iBAAiB,4CAgEnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"BotMenuSheet.d.ts","sourceRoot":"","sources":["../../../../src/components/conversation-detail/bot/BotMenuSheet.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAI5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;CAC3C;AAED,QAAA,MAAM,YAAY,GAAI,+CAMnB,iBAAiB,4CAiEnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -17,7 +17,7 @@ const BotMenuSheet = ({ open, items, disabled, onClose, onSelect, }) => {
17
17
  body: { padding: "16px 16px 32px" },
18
18
  mask: { backgroundColor: "rgba(0, 0, 0, 0.8)" },
19
19
  content: { background: "transparent", boxShadow: "none" },
20
- }, getContainer: false, children: _jsxs("div", { className: "flex flex-col overflow-hidden rounded-2xl bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("thread_detail.bot.menu.title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("thread_detail.bot.menu.title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsx("div", { className: "flex flex-col", children: items.map((item) => (_jsxs("button", { type: "button", disabled: disabled, onClick: () => handleSelect(item), className: "flex items-center gap-4 border-b border-gray-25 px-3 py-3 text-left transition-colors last:border-b-0 disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx("span", { className: "flex-1 text-[16px] font-medium leading-[1.6] text-gray-900", children: item.id === BOT_DEFAULT_BUTTON_ID
20
+ }, getContainer: false, forceRender: true, children: _jsxs("div", { className: "flex flex-col overflow-hidden rounded-2xl bg-white", children: [_jsxs("div", { className: "flex items-center gap-4 border-b border-gray-25 px-3 py-4", children: [_jsx("span", { className: "flex-1 text-[18px] font-bold leading-[1.6] text-gray-900", children: t("thread_detail.bot.menu.title") }), _jsx(Button, { type: "text", shape: "default", className: "h-6 w-6 p-0 text-gray-900", onClick: onClose, "aria-label": t("thread_detail.bot.menu.title"), children: _jsx(Icon, { icon: "close-b", size: 24 }) })] }), _jsx("div", { className: "flex flex-col", children: items.map((item) => (_jsxs("button", { type: "button", disabled: disabled, onClick: () => handleSelect(item), className: "flex items-center gap-4 border-b border-gray-25 px-3 py-3 text-left transition-colors last:border-b-0 disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx("span", { className: "flex-1 text-[16px] font-medium leading-[1.6] text-gray-900", children: item.id === BOT_DEFAULT_BUTTON_ID
21
21
  ? t("thread_detail.bot.restart")
22
22
  : item.label }), _jsx(Icon, { icon: "angle-right-o", size: CHEVRON_SIZE, className: "text-gray-900" })] }, item.id))) })] }) }));
23
23
  };
@@ -1 +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,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,cAAc,GAAI,6BAIrB,mBAAmB,4CA2LrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
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,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,cAAc,GAAI,6BAIrB,mBAAmB,4CA4LrB,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -60,7 +60,7 @@ const AddMemberModal = ({ open, groupID, onClose, }) => {
60
60
  message.error(errorMessage);
61
61
  }
62
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) => {
63
+ return (_jsx(Modal, { centered: true, open: open, onCancel: handleClose, footer: null, title: t("add_member.title"), getContainer: false, forceRender: true, 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
64
  const isSelected = selectedIds.has(account.userID);
65
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
66
  ? "border-primary-400 bg-primary-400"
@@ -1,9 +1,15 @@
1
+ interface AdminPermissionModalMember {
2
+ userID: string;
3
+ permissions: string[];
4
+ }
1
5
  interface AdminPermissionModalProps {
2
6
  open: boolean;
3
7
  groupID: string;
4
8
  onClose: () => void;
5
9
  inline?: boolean;
10
+ member?: AdminPermissionModalMember;
11
+ onMemberPermissionsSaved?: (userID: string, permissions: string[]) => void;
6
12
  }
7
- declare const AdminPermissionModal: ({ open, groupID, onClose, inline, }: AdminPermissionModalProps) => import("react/jsx-runtime").JSX.Element | null;
13
+ declare const AdminPermissionModal: ({ open, groupID, onClose, inline, member, onMemberPermissionsSaved, }: AdminPermissionModalProps) => import("react/jsx-runtime").JSX.Element | null;
8
14
  export default AdminPermissionModal;
9
15
  //# sourceMappingURL=AdminPermissionModal.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"AdminPermissionModal.d.ts","sourceRoot":"","sources":["../../../src/components/group/AdminPermissionModal.tsx"],"names":[],"mappings":"AAcA,UAAU,0BAA0B;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,UAAU,yBAAyB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IAIpB,MAAM,CAAC,EAAE,OAAO,CAAC;IAKjB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IAKpC,wBAAwB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAC5E;AA8CD,QAAA,MAAM,oBAAoB,GAAI,uEAO3B,yBAAyB,mDA0L3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -4,53 +4,108 @@ import { useEffect, useState } from "react";
4
4
  import { ConfigProvider, Modal, Switch, message } from "antd";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import { useGroupPermission } from "../../hooks/group/useGroupPermission";
7
+ import { useSetGroupMemberPermissions } from "../../hooks/group/useGroupMemberActions";
7
8
  import { Icon } from "../../components/icon";
8
9
  import { PERMISSION_SWITCH_COLOR } from "../../constants";
10
+ import { GROUP_PERMISSION_KEY } from "../../constants/im";
9
11
  import useGroupPermissionOverridesStore from "../../store/groupPermissionOverrides";
12
+ const permissionListToOverrides = (permissionList) => ({
13
+ sendMessage: permissionList.includes(GROUP_PERMISSION_KEY.sendMessage),
14
+ pinMessage: permissionList.includes(GROUP_PERMISSION_KEY.pinMessage),
15
+ viewMemberInformation: permissionList.includes(GROUP_PERMISSION_KEY.viewMemberInformation),
16
+ addMember: permissionList.includes(GROUP_PERMISSION_KEY.addMember),
17
+ removeMember: permissionList.includes(GROUP_PERMISSION_KEY.removeMember),
18
+ updateGroupInfo: permissionList.includes(GROUP_PERMISSION_KEY.updateGroupName),
19
+ });
20
+ const overridesToPermissionList = (overrides, currentPermissionList) => {
21
+ const desired = {
22
+ [GROUP_PERMISSION_KEY.sendMessage]: overrides.sendMessage,
23
+ [GROUP_PERMISSION_KEY.pinMessage]: overrides.pinMessage,
24
+ [GROUP_PERMISSION_KEY.viewMemberInformation]: overrides.viewMemberInformation,
25
+ [GROUP_PERMISSION_KEY.addMember]: overrides.addMember,
26
+ [GROUP_PERMISSION_KEY.removeMember]: overrides.removeMember,
27
+ [GROUP_PERMISSION_KEY.updateGroupName]: overrides.updateGroupInfo,
28
+ };
29
+ const managedKeys = new Set(Object.keys(desired));
30
+ const untouched = currentPermissionList.filter((permission) => !managedKeys.has(permission));
31
+ const enabled = Object.entries(desired)
32
+ .filter(([, isEnabled]) => isEnabled)
33
+ .map(([permission]) => permission);
34
+ return [...untouched, ...enabled];
35
+ };
10
36
  // 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, }) => {
37
+ // when used group-wide (no `member`, e.g. from GroupThreadInfo) applies
38
+ // to the whole Admin role in the group, no BE persistence yet, committed to
39
+ // the in-memory override store on "Lưu" only. When `member` is passed
40
+ // (from the member list's per-row menu), it instead patches that member's
41
+ // own permission list via the same setGroupMemberInfo call used to mute a
42
+ // member — real BE persistence, scoped to that user.
43
+ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member, onMemberPermissionsSaved, }) => {
15
44
  const { t } = useTranslation();
16
- const permissions = useGroupPermission(groupID);
45
+ const groupPermissions = useGroupPermission(groupID);
17
46
  const setAdminOverrides = useGroupPermissionOverridesStore((state) => state.setAdminOverrides);
47
+ const { mutateAsync: setMemberPermissions } = useSetGroupMemberPermissions();
18
48
  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
- });
49
+ const initialOverrides = () => member
50
+ ? permissionListToOverrides(member.permissions)
51
+ : {
52
+ sendMessage: groupPermissions.sendMessage,
53
+ pinMessage: groupPermissions.pinMessage,
54
+ viewMemberInformation: groupPermissions.viewMemberInformation,
55
+ addMember: groupPermissions.addMember,
56
+ removeMember: groupPermissions.removeMember,
57
+ updateGroupInfo: groupPermissions.updateGroupName,
58
+ };
59
+ const [pending, setPending] = useState(initialOverrides);
27
60
  useEffect(() => {
28
61
  if (!open)
29
62
  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
- });
63
+ setPending(initialOverrides());
38
64
  // 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;
65
+ }, [open, member === null || member === void 0 ? void 0 : member.userID]);
66
+ const baseline = member
67
+ ? permissionListToOverrides(member.permissions)
68
+ : {
69
+ sendMessage: groupPermissions.sendMessage,
70
+ pinMessage: groupPermissions.pinMessage,
71
+ viewMemberInformation: groupPermissions.viewMemberInformation,
72
+ addMember: groupPermissions.addMember,
73
+ removeMember: groupPermissions.removeMember,
74
+ updateGroupInfo: groupPermissions.updateGroupName,
75
+ };
76
+ const hasChanges = pending.sendMessage !== baseline.sendMessage ||
77
+ pending.pinMessage !== baseline.pinMessage ||
78
+ pending.viewMemberInformation !== baseline.viewMemberInformation ||
79
+ pending.addMember !== baseline.addMember ||
80
+ pending.removeMember !== baseline.removeMember ||
81
+ pending.updateGroupInfo !== baseline.updateGroupInfo;
46
82
  const handleSave = async () => {
83
+ var _a, _b;
47
84
  if (!groupID)
48
85
  return;
49
86
  setIsSaving(true);
50
- setAdminOverrides(groupID, pending);
51
- message.success(t("update_admin_permissions_success"));
52
- setIsSaving(false);
53
- onClose();
87
+ try {
88
+ if (member) {
89
+ const nextPermissions = overridesToPermissionList(pending, member.permissions);
90
+ await setMemberPermissions({
91
+ groupID,
92
+ userID: member.userID,
93
+ permissions: nextPermissions,
94
+ });
95
+ onMemberPermissionsSaved === null || onMemberPermissionsSaved === void 0 ? void 0 : onMemberPermissionsSaved(member.userID, nextPermissions);
96
+ }
97
+ else {
98
+ setAdminOverrides(groupID, pending);
99
+ }
100
+ message.success(t("update_admin_permissions_success"));
101
+ onClose();
102
+ }
103
+ catch (error) {
104
+ message.error(((_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("update_group_info_failed"));
105
+ }
106
+ finally {
107
+ setIsSaving(false);
108
+ }
54
109
  };
55
110
  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
111
  if (inline) {
@@ -58,6 +113,6 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, }) => {
58
113
  return null;
59
114
  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
115
  }
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 }) }));
116
+ 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, forceRender: true, children: _jsx("div", { className: "mt-2", children: rows }) }));
62
117
  };
63
118
  export default AdminPermissionModal;
@@ -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;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,YAAY,oBAAoB,4CAqKxD,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"CreateGroupForm.d.ts","sourceRoot":"","sources":["../../../src/components/group/CreateGroupForm.tsx"],"names":[],"mappings":"AAeA,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,YAAY,oBAAoB,4CAsNxD,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,32 +1,66 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button, Input, Upload } from "antd";
3
+ import { Button, Input, Upload, message } from "antd";
4
4
  import clsx from "clsx";
5
- import { useMemo, useState } from "react";
5
+ import { useEffect, useMemo, useRef, useState } from "react";
6
6
  import { useTranslation } from "react-i18next";
7
7
  import { Icon } from "../../components/icon";
8
8
  import { useCreateGroup } from "../../hooks/group/useCreateGroup";
9
+ import { useUploadFile } from "../../hooks/common/useUploadFile";
10
+ import { validateFile } from "../../utils/fileValidation";
9
11
  import GroupMemberPicker from "./GroupMemberPicker";
10
12
  const GROUP_NAME_MAX_LENGTH = 255;
11
13
  const CreateGroupForm = ({ onDone }) => {
12
14
  const { t } = useTranslation("conversation-inbox");
15
+ const { t: tCommon } = useTranslation("common");
13
16
  const { createGroup } = useCreateGroup();
17
+ const { mutate: uploadFile, isPending: isAvatarUploading } = useUploadFile();
14
18
  const [groupName, setGroupName] = useState("");
15
19
  // AC-Privacy1: only set at creation time, not editable after — no need
16
20
  // for an "update privacy" path anywhere else in this modal.
17
21
  const [isPublic, setIsPublic] = useState(false);
18
- const [avatarFile, setAvatarFile] = useState(null);
22
+ // Local blob URL for instant preview while the real upload is in flight.
19
23
  const [avatarPreviewUrl, setAvatarPreviewUrl] = useState("");
24
+ // Real, server-hosted URL — this is what actually gets sent as faceURL.
25
+ const [avatarFaceURL, setAvatarFaceURL] = useState("");
20
26
  const [selectedMembers, setSelectedMembers] = useState([]);
21
27
  const [isSubmitting, setIsSubmitting] = useState(false);
22
- const canSubmit = selectedMembers.length > 0 && !isSubmitting;
28
+ const avatarPreviewUrlRef = useRef("");
29
+ useEffect(() => {
30
+ avatarPreviewUrlRef.current = avatarPreviewUrl;
31
+ }, [avatarPreviewUrl]);
32
+ // Revoke whatever blob URL is live when the modal unmounts.
33
+ useEffect(() => () => {
34
+ if (avatarPreviewUrlRef.current) {
35
+ URL.revokeObjectURL(avatarPreviewUrlRef.current);
36
+ }
37
+ }, []);
38
+ const isGroupNameValid = groupName.trim().length > 0;
39
+ const canSubmit = isGroupNameValid &&
40
+ selectedMembers.length > 0 &&
41
+ !isSubmitting &&
42
+ !isAvatarUploading;
23
43
  const handleAvatarSelect = (file) => {
24
- // Local preview only — there's no dedicated avatar-upload endpoint yet.
25
- // faceURL falls back to this object URL until BE provides one; group
26
- // creation itself doesn't block on it (Q3/Q4-style non-block gap).
44
+ const validation = validateFile(file, tCommon);
45
+ if (!validation.isValid) {
46
+ message.error(validation.error);
47
+ return false;
48
+ }
27
49
  const previewUrl = URL.createObjectURL(file);
50
+ if (avatarPreviewUrlRef.current) {
51
+ URL.revokeObjectURL(avatarPreviewUrlRef.current);
52
+ }
28
53
  setAvatarPreviewUrl(previewUrl);
29
- setAvatarFile({ uid: file.name, name: file.name });
54
+ setAvatarFaceURL("");
55
+ uploadFile(file, {
56
+ onSuccess: (uploaded) => {
57
+ setAvatarFaceURL((uploaded === null || uploaded === void 0 ? void 0 : uploaded.publicUrl) || "");
58
+ },
59
+ onError: () => {
60
+ message.error(t("create_group.avatar_upload_failed"));
61
+ setAvatarPreviewUrl("");
62
+ },
63
+ });
30
64
  return false;
31
65
  };
32
66
  const handleSubmit = async () => {
@@ -38,7 +72,7 @@ const CreateGroupForm = ({ onDone }) => {
38
72
  groupName,
39
73
  isPublic,
40
74
  members: selectedMembers,
41
- faceURL: avatarPreviewUrl,
75
+ faceURL: avatarFaceURL,
42
76
  });
43
77
  onDone();
44
78
  }
@@ -65,7 +99,7 @@ const CreateGroupForm = ({ onDone }) => {
65
99
  ], [t]);
66
100
  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
101
  // 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-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
102
+ _jsx("img", { src: avatarPreviewUrl, alt: "", className: clsx("h-full w-full rounded-2xl object-cover", isAvatarUploading && "opacity-50") })) : (_jsx(Icon, { icon: "user-three-b", size: 32, className: "text-primary-400" })), isAvatarUploading && (_jsx("span", { className: "absolute inset-0 flex items-center justify-center rounded-2xl bg-black/10", children: _jsx("span", { className: "h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent" }) })), _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
103
  ? "border-primary-400 bg-primary-50"
70
104
  : "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
105
  ? "text-primary-400"
@@ -1 +1 @@
1
- {"version":3,"file":"GroupCompositeAvatar.d.ts","sourceRoot":"","sources":["../../../src/components/group/GroupCompositeAvatar.tsx"],"names":[],"mappings":"AAOA,UAAU,yBAAyB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD,QAAA,MAAM,oBAAoB,GAAI,gEAO3B,yBAAyB,4CAyD3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"GroupCompositeAvatar.d.ts","sourceRoot":"","sources":["../../../src/components/group/GroupCompositeAvatar.tsx"],"names":[],"mappings":"AAOA,UAAU,yBAAyB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAOD,QAAA,MAAM,oBAAoB,GAAI,gEAO3B,yBAAyB,4CA2E3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -13,10 +13,17 @@ const GroupCompositeAvatar = ({ groupID, faceURL, ownerUserID, groupName, size =
13
13
  var _a, _b;
14
14
  const shouldLoadMembers = !faceURL && Boolean(groupID);
15
15
  const { members } = useGroupMembers(shouldLoadMembers ? groupID : undefined);
16
- const { ownerFaceURL, firstMemberFaceURL } = useMemo(() => {
17
- var _a;
18
- if (!members.length)
19
- return { ownerFaceURL: undefined, firstMemberFaceURL: undefined };
16
+ const { ownerFaceURL, ownerLetter, hasFirstMember, firstMemberFaceURL, firstMemberLetter } = useMemo(() => {
17
+ var _a, _b, _c, _d, _e;
18
+ if (!members.length) {
19
+ return {
20
+ ownerFaceURL: undefined,
21
+ ownerLetter: undefined,
22
+ hasFirstMember: false,
23
+ firstMemberFaceURL: undefined,
24
+ firstMemberLetter: undefined,
25
+ };
26
+ }
20
27
  // Prefer the explicit ownerUserID when the caller has it (e.g. GroupItem);
21
28
  // otherwise fall back to roleLevel, which every member list already carries.
22
29
  const owner = (_a = members.find((member) => member.userID === ownerUserID)) !== null && _a !== void 0 ? _a : members.find((member) => member.roleLevel === GroupMemberRole.Owner);
@@ -25,16 +32,21 @@ const GroupCompositeAvatar = ({ groupID, faceURL, ownerUserID, groupName, size =
25
32
  .sort((a, b) => a.joinTime - b.joinTime)[0];
26
33
  return {
27
34
  ownerFaceURL: owner === null || owner === void 0 ? void 0 : owner.faceURL,
35
+ ownerLetter: (_c = (_b = owner === null || owner === void 0 ? void 0 : owner.nickname) === null || _b === void 0 ? void 0 : _b.charAt) === null || _c === void 0 ? void 0 : _c.call(_b, 0),
36
+ hasFirstMember: Boolean(firstMember),
28
37
  firstMemberFaceURL: firstMember === null || firstMember === void 0 ? void 0 : firstMember.faceURL,
38
+ firstMemberLetter: (_e = (_d = firstMember === null || firstMember === void 0 ? void 0 : firstMember.nickname) === null || _d === void 0 ? void 0 : _d.charAt) === null || _e === void 0 ? void 0 : _e.call(_d, 0),
29
39
  };
30
40
  }, [members, ownerUserID]);
31
41
  if (faceURL) {
32
42
  return (_jsx(Avatar, { src: faceURL, size: size, className: className, children: ((_a = groupName === null || groupName === void 0 ? void 0 : groupName.charAt) === null || _a === void 0 ? void 0 : _a.call(groupName, 0)) || "G" }));
33
43
  }
34
- if (!shouldLoadMembers || (!ownerFaceURL && !firstMemberFaceURL)) {
35
- return (_jsx(Avatar, { size: size, className: className, children: ((_b = groupName === null || groupName === void 0 ? void 0 : groupName.charAt) === null || _b === void 0 ? void 0 : _b.call(groupName, 0)) || "G" }));
44
+ // Not enough members yet to compose two avatars (still loading, or a
45
+ // solo group with no other member) fall back to a single placeholder.
46
+ if (!shouldLoadMembers || !hasFirstMember) {
47
+ return (_jsx(Avatar, { size: size, className: className, children: ownerLetter || ((_b = groupName === null || groupName === void 0 ? void 0 : groupName.charAt) === null || _b === void 0 ? void 0 : _b.call(groupName, 0)) || "G" }));
36
48
  }
37
49
  const subSize = Math.round(size * 0.62);
38
- return (_jsxs("div", { className: className, style: { position: "relative", width: size, height: size, flexShrink: 0 }, children: [_jsx(Avatar, { src: ownerFaceURL, size: subSize, style: { position: "absolute", left: 0, bottom: 0, border: "2px solid white" } }), _jsx(Avatar, { src: firstMemberFaceURL, size: subSize, style: { position: "absolute", right: 0, top: 0, border: "2px solid white" } })] }));
50
+ return (_jsxs("div", { className: className, style: { position: "relative", width: size, height: size, flexShrink: 0 }, children: [_jsx(Avatar, { src: ownerFaceURL, size: subSize, style: { position: "absolute", left: 0, bottom: 0, border: "2px solid white" }, children: ownerLetter || "A" }), _jsx(Avatar, { src: firstMemberFaceURL, size: subSize, style: { position: "absolute", right: 0, top: 0, border: "2px solid white" }, children: firstMemberLetter || "A" })] }));
39
51
  };
40
52
  export default GroupCompositeAvatar;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/media-collection/index.tsx"],"names":[],"mappings":"AAaA,oBAAY,qBAAqB;IAC/B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;CACd;AAED,QAAA,MAAM,eAAe,+CA8EpB,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/media-collection/index.tsx"],"names":[],"mappings":"AAaA,oBAAY,qBAAqB;IAC/B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,IAAI,SAAS;IACb,IAAI,SAAS;CACd;AAED,QAAA,MAAM,eAAe,+CA+EpB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useBoolean } from "ahooks";
4
4
  import { Button, Drawer, Tabs } from "antd";
5
5
  import { Icon } from "../../components/icon";
@@ -47,11 +47,11 @@ const MediaCollection = () => {
47
47
  },
48
48
  ];
49
49
  }, [isOpen, t]);
50
- return (_jsxs(_Fragment, { children: [_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "folder-o", size: 22 }) }), _jsx(Drawer, { open: isOpen, onClose: toggle, mask: false, closeIcon: false, styles: {
50
+ return (_jsxs("div", { className: "inline-flex shrink-0", children: [_jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "folder-o", size: 22 }) }), _jsx(Drawer, { open: isOpen, onClose: toggle, mask: false, closeIcon: false, styles: {
51
51
  body: {
52
52
  padding: 0,
53
53
  height: "100%",
54
54
  },
55
- }, getContainer: false, width: isMobile ? "100%" : 360, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsx("span", { className: "text-lg font-medium", children: t("library") }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "close-b", size: 22 }) })] }), _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(Tabs, { defaultActiveKey: MediaCollectionTabKey.Image, items: items, className: "h-full" }) })] }) })] }));
55
+ }, getContainer: false, forceRender: true, width: isMobile ? "100%" : 360, children: _jsxs("div", { className: "flex flex-col h-full", children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsx("span", { className: "text-lg font-medium", children: t("library") }), _jsx(Button, { type: "text", shape: "default", className: "text-gray-500 w-8 h-8 p-0", onClick: toggle, children: _jsx(Icon, { icon: "close-b", size: 22 }) })] }), _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(Tabs, { defaultActiveKey: MediaCollectionTabKey.Image, items: items, className: "h-full" }) })] }) })] }));
56
56
  };
57
57
  export default MediaCollection;
@@ -1 +1 @@
1
- {"version":3,"file":"MediaResourcePanel.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaResourcePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,gCAIzB,uBAAuB,4CAoCzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"MediaResourcePanel.d.ts","sourceRoot":"","sources":["../../../src/components/media-resources/MediaResourcePanel.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,uBAAuB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,mBAAmB,CAAC;IAChC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,QAAA,MAAM,kBAAkB,GAAI,gCAIzB,uBAAuB,4CAqCzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -8,6 +8,6 @@ const MediaResourcePanel = ({ open, initialTab, onClose, }) => {
8
8
  const { t } = useTranslation();
9
9
  return (_jsx(Drawer, { open: open, onClose: onClose, placement: "right", closeIcon: false, width: "100%", styles: {
10
10
  body: { padding: 0 },
11
- }, getContainer: false, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex w-full items-center p-2", children: [_jsx("button", { type: "button", "data-testid": "media-resource-back", 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("media_resources_title") })] }), _jsx("div", { className: "min-h-0 flex-1", children: _jsx(MediaResourceTabs, { initialTab: initialTab }) })] }) }));
11
+ }, getContainer: false, forceRender: true, destroyOnHidden: true, children: _jsxs("div", { className: "flex h-full flex-col bg-white", children: [_jsxs("div", { className: "flex w-full items-center p-2", children: [_jsx("button", { type: "button", "data-testid": "media-resource-back", 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("media_resources_title") })] }), _jsx("div", { className: "min-h-0 flex-1", children: _jsx(MediaResourceTabs, { initialTab: initialTab }) })] }) }));
12
12
  };
13
13
  export default MediaResourcePanel;
@@ -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;AAgB/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,4CA6ezB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
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,4CAyhBzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}