@droppii-org/chat-sdk 0.3.0 → 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.
- package/dist/components/conversation/ConversationByGroupItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationByGroupItem.js +2 -3
- package/dist/components/conversation/ConversationBySessionItem.d.ts.map +1 -1
- package/dist/components/conversation/ConversationBySessionItem.js +3 -1
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +1 -1
- package/dist/components/conversation-detail/PinnedMessagesPanel.d.ts.map +1 -1
- package/dist/components/conversation-detail/PinnedMessagesPanel.js +1 -1
- package/dist/components/conversation-detail/ThreadProfilePanel.d.ts.map +1 -1
- package/dist/components/conversation-detail/ThreadProfilePanel.js +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.d.ts.map +1 -1
- package/dist/components/conversation-detail/bot/BotMenuSheet.js +1 -1
- package/dist/components/group/AddMemberModal.d.ts.map +1 -1
- package/dist/components/group/AddMemberModal.js +1 -1
- package/dist/components/group/AdminPermissionModal.d.ts +7 -1
- package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
- package/dist/components/group/AdminPermissionModal.js +88 -33
- package/dist/components/group/CreateGroupForm.d.ts.map +1 -1
- package/dist/components/group/CreateGroupForm.js +44 -10
- package/dist/components/group/GroupCompositeAvatar.d.ts +11 -0
- package/dist/components/group/GroupCompositeAvatar.d.ts.map +1 -0
- package/dist/components/group/GroupCompositeAvatar.js +52 -0
- package/dist/components/media-collection/index.d.ts.map +1 -1
- package/dist/components/media-collection/index.js +3 -3
- package/dist/components/media-resources/MediaResourcePanel.d.ts.map +1 -1
- package/dist/components/media-resources/MediaResourcePanel.js +1 -1
- package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
- package/dist/components/message/GroupMembersDrawer.js +57 -29
- package/dist/components/message/MessageHeader.d.ts.map +1 -1
- package/dist/components/message/MessageHeader.js +17 -5
- package/dist/components/message/item/GroupNotificationMessage.d.ts +7 -0
- package/dist/components/message/item/GroupNotificationMessage.d.ts.map +1 -0
- package/dist/components/message/item/GroupNotificationMessage.js +13 -0
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +10 -2
- package/dist/components/message/item/renderMessageContent.d.ts.map +1 -1
- package/dist/components/message/item/renderMessageContent.js +18 -0
- package/dist/components/search-conversation/SearchDrawer.d.ts.map +1 -1
- package/dist/components/search-conversation/SearchDrawer.js +1 -1
- package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/GroupThreadInfo.js +36 -5
- package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/SingleThreadInfo.js +1 -1
- package/dist/components/thread/ThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/ThreadInfo.js +2 -1
- package/dist/components/video-player/VideoPlayerModal.d.ts.map +1 -1
- package/dist/components/video-player/VideoPlayerModal.js +1 -1
- package/dist/hooks/common/useUploadFile.d.ts +3 -0
- package/dist/hooks/common/useUploadFile.d.ts.map +1 -0
- package/dist/hooks/common/useUploadFile.js +15 -0
- package/dist/hooks/group/useCreateGroup.d.ts.map +1 -1
- package/dist/hooks/group/useCreateGroup.js +4 -2
- package/dist/hooks/group/useGroupMembers.d.ts +3 -1
- package/dist/hooks/group/useGroupMembers.d.ts.map +1 -1
- package/dist/hooks/group/useGroupMembers.js +9 -0
- package/dist/hooks/message/useMessage.d.ts.map +1 -1
- package/dist/hooks/message/useMessage.js +2 -1
- package/dist/locales/vi/common.json +1 -0
- package/dist/locales/vi/conversation-inbox.json +4 -2
- package/dist/services/routes.d.ts +3 -0
- package/dist/services/routes.d.ts.map +1 -1
- package/dist/services/routes.js +3 -0
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +10 -0
- package/dist/store/session.d.ts +13 -1
- package/dist/store/session.d.ts.map +1 -1
- package/dist/store/session.js +6 -2
- package/dist/styles/global.css +1 -1
- package/dist/types/dto.d.ts +11 -0
- package/dist/types/dto.d.ts.map +1 -1
- package/dist/utils/imCommon.d.ts +4 -0
- package/dist/utils/imCommon.d.ts.map +1 -1
- package/dist/utils/imCommon.js +16 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConversationByGroupItem.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/ConversationByGroupItem.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAOxF,UAAU,4BAA4B;IACpC,KAAK,EAAE,4BAA4B,CAAC;IACpC,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,QAAA,MAAM,uBAAuB,GAAI,2BAG9B,4BAA4B,4CA8D9B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Avatar } from "antd";
|
|
4
3
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
5
4
|
import { useTranslation } from "react-i18next";
|
|
6
5
|
import useConversationStore from "../../store/conversation";
|
|
7
6
|
import { useChatContext } from "../../context/ChatContext";
|
|
8
7
|
import { formatTimestamp, parseLatestMessage } from "../../utils/common";
|
|
8
|
+
import GroupCompositeAvatar from "../../components/group/GroupCompositeAvatar";
|
|
9
9
|
import ThreadCard from "./ThreadCard";
|
|
10
10
|
const ConversationByGroupItem = ({ group, latestPreview, }) => {
|
|
11
|
-
var _a;
|
|
12
11
|
const { t } = useTranslation();
|
|
13
12
|
const user = useChatContext().user;
|
|
14
13
|
const router = useRouter();
|
|
@@ -35,6 +34,6 @@ const ConversationByGroupItem = ({ group, latestPreview, }) => {
|
|
|
35
34
|
showName: conversation.showName,
|
|
36
35
|
}), time: latestMsgSendTime > 0
|
|
37
36
|
? formatTimestamp(latestMsgSendTime, { hasTime: true })
|
|
38
|
-
: undefined, avatar: _jsx(
|
|
37
|
+
: undefined, avatar: _jsx(GroupCompositeAvatar, { groupID: group.groupId, faceURL: conversation.faceURL, groupName: conversation.showName, size: 48 }), title: _jsx("h3", { className: "truncate text-sm font-semibold text-gray-900", children: conversation.showName }) }));
|
|
39
38
|
};
|
|
40
39
|
export default ConversationByGroupItem;
|
|
@@ -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;
|
|
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,+
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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":"
|
|
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
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// override store on "Lưu" only.
|
|
14
|
-
|
|
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
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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":"
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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:
|
|
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"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface GroupCompositeAvatarProps {
|
|
2
|
+
groupID?: string;
|
|
3
|
+
faceURL?: string;
|
|
4
|
+
ownerUserID?: string;
|
|
5
|
+
groupName?: string;
|
|
6
|
+
size?: number;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const GroupCompositeAvatar: ({ groupID, faceURL, ownerUserID, groupName, size, className, }: GroupCompositeAvatarProps) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export default GroupCompositeAvatar;
|
|
11
|
+
//# sourceMappingURL=GroupCompositeAvatar.d.ts.map
|
|
@@ -0,0 +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,4CA2E3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Avatar } from "antd";
|
|
4
|
+
import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
import { useGroupMembers } from "../../hooks/group/useGroupMembers";
|
|
7
|
+
// AC-Default2 (DROPPII-29976): when a group is created without a picked
|
|
8
|
+
// avatar, show two overlapping avatars (owner bottom-left, first-joined
|
|
9
|
+
// non-owner member top-right) instead of a static placeholder image —
|
|
10
|
+
// resolved client-side so it always reflects current member avatars,
|
|
11
|
+
// no image compositing/upload needed.
|
|
12
|
+
const GroupCompositeAvatar = ({ groupID, faceURL, ownerUserID, groupName, size = 56, className, }) => {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
const shouldLoadMembers = !faceURL && Boolean(groupID);
|
|
15
|
+
const { members } = useGroupMembers(shouldLoadMembers ? groupID : 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
|
+
}
|
|
27
|
+
// Prefer the explicit ownerUserID when the caller has it (e.g. GroupItem);
|
|
28
|
+
// otherwise fall back to roleLevel, which every member list already carries.
|
|
29
|
+
const owner = (_a = members.find((member) => member.userID === ownerUserID)) !== null && _a !== void 0 ? _a : members.find((member) => member.roleLevel === GroupMemberRole.Owner);
|
|
30
|
+
const firstMember = [...members]
|
|
31
|
+
.filter((member) => member.userID !== (owner === null || owner === void 0 ? void 0 : owner.userID))
|
|
32
|
+
.sort((a, b) => a.joinTime - b.joinTime)[0];
|
|
33
|
+
return {
|
|
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),
|
|
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),
|
|
39
|
+
};
|
|
40
|
+
}, [members, ownerUserID]);
|
|
41
|
+
if (faceURL) {
|
|
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" }));
|
|
43
|
+
}
|
|
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" }));
|
|
48
|
+
}
|
|
49
|
+
const subSize = Math.round(size * 0.62);
|
|
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" })] }));
|
|
51
|
+
};
|
|
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,+
|
|
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
|
|
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(
|
|
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,
|
|
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;
|
|
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"}
|