@droppii-org/chat-sdk 0.3.5 → 0.3.7
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/common/ConfirmModal.d.ts +18 -0
- package/dist/components/common/ConfirmModal.d.ts.map +1 -0
- package/dist/components/common/ConfirmModal.js +15 -0
- package/dist/components/conversation/DeskConversationList.d.ts.map +1 -1
- package/dist/components/conversation/DeskConversationList.js +20 -14
- package/dist/components/group/AdminPermissionModal.d.ts.map +1 -1
- package/dist/components/group/AdminPermissionModal.js +37 -13
- package/dist/components/message/GroupMembersDrawer.d.ts.map +1 -1
- package/dist/components/message/GroupMembersDrawer.js +27 -14
- package/dist/components/message/MessageList.d.ts.map +1 -1
- package/dist/components/message/MessageList.js +17 -3
- package/dist/components/message/item/MessageBubble.d.ts +3 -2
- package/dist/components/message/item/MessageBubble.d.ts.map +1 -1
- package/dist/components/message/item/MessageBubble.js +8 -2
- package/dist/components/message/item/index.d.ts.map +1 -1
- package/dist/components/message/item/index.js +22 -7
- package/dist/components/thread/GroupThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/GroupThreadInfo.js +43 -14
- package/dist/components/thread/SingleThreadInfo.d.ts.map +1 -1
- package/dist/components/thread/SingleThreadInfo.js +3 -2
- package/dist/hooks/global/useGlobalEvent.d.ts.map +1 -1
- package/dist/hooks/global/useGlobalEvent.js +32 -3
- package/dist/hooks/group/useGroupPermission.js +1 -1
- package/dist/hooks/session/useConversationSessionState.d.ts.map +1 -1
- package/dist/hooks/session/useConversationSessionState.js +1 -0
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +5 -0
- package/dist/locales/vi/common.json +14 -8
- package/dist/store/conversation.d.ts.map +1 -1
- package/dist/store/conversation.js +16 -4
- package/dist/styles/global.css +1 -1
- package/dist/utils/common.d.ts +1 -0
- package/dist/utils/common.d.ts.map +1 -1
- package/dist/utils/common.js +18 -0
- package/dist/utils/fileValidation.d.ts.map +1 -1
- package/dist/utils/fileValidation.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export interface ConfirmModalProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
title: ReactNode;
|
|
5
|
+
okText: ReactNode;
|
|
6
|
+
cancelText: ReactNode;
|
|
7
|
+
onOk: () => void;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
okType?: "danger" | "primary";
|
|
10
|
+
confirmLoading?: boolean;
|
|
11
|
+
okButtonProps?: {
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
};
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
declare const ConfirmModal: ({ open, title, okText, cancelText, onOk, onCancel, okType, confirmLoading, okButtonProps, children, }: ConfirmModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export default ConfirmModal;
|
|
18
|
+
//# sourceMappingURL=ConfirmModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmModal.d.ts","sourceRoot":"","sources":["../../../src/components/common/ConfirmModal.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,SAAS,CAAC;IACtB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,QAAA,MAAM,YAAY,GAAI,uGAWnB,iBAAiB,4CAiCnB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Modal } from "antd";
|
|
4
|
+
const ConfirmModal = ({ open, title, okText, cancelText, onOk, onCancel, okType = "primary", confirmLoading, okButtonProps, children, }) => {
|
|
5
|
+
return (_jsx(Modal, { centered: true, open: open, onOk: onOk, onCancel: onCancel, okText: okText, cancelText: cancelText, confirmLoading: confirmLoading, okButtonProps: Object.assign(Object.assign({}, okButtonProps), { className: okType === "danger"
|
|
6
|
+
? "!h-10 !rounded-lg !border-danger-400 !bg-danger-400 !text-white hover:!border-danger-300 hover:!bg-danger-300"
|
|
7
|
+
: "!h-10 !rounded-lg !border-primary-400 !bg-primary-400 !text-white hover:!border-primary-300 hover:!bg-primary-300" }), cancelButtonProps: {
|
|
8
|
+
className: "!h-10 !rounded-lg !border-gray-25 !bg-gray-25 !text-gray-900",
|
|
9
|
+
}, styles: {
|
|
10
|
+
header: { textAlign: "center", marginBottom: 0 },
|
|
11
|
+
footer: { marginTop: 16 },
|
|
12
|
+
body: { paddingTop: 4 },
|
|
13
|
+
}, classNames: { footer: "border-t border-gray-25 pt-4" }, title: _jsx("span", { className: "text-[18px] font-bold text-gray-900", children: title }), getContainer: false, forceRender: true, children: children }));
|
|
14
|
+
};
|
|
15
|
+
export default ConfirmModal;
|
|
@@ -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,+CAySzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -68,20 +68,26 @@ const DeskConversationList = () => {
|
|
|
68
68
|
}, [showSearch]);
|
|
69
69
|
useEffect(() => {
|
|
70
70
|
const threadId = searchParams.get("threadId");
|
|
71
|
-
if (threadId) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
71
|
+
if (!threadId) {
|
|
72
|
+
useConversationStore.getState().clearSelectedConversation();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
setSelectedConversationId(threadId);
|
|
76
|
+
const selectedConversation = conversationList.find((conv) => conv.conversationID === threadId);
|
|
77
|
+
if (selectedConversation) {
|
|
78
|
+
setConversationData(selectedConversation);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
DChatSDK.getMultipleConversation([threadId]).then((res) => {
|
|
82
|
+
// threadId can go stale between the fetch starting and resolving
|
|
83
|
+
// (e.g. the user was removed from that group mid-flight) — only
|
|
84
|
+
// apply the result if the URL still points at this conversation.
|
|
85
|
+
if (res.data.length > 0 &&
|
|
86
|
+
searchParams.get("threadId") === threadId) {
|
|
87
|
+
setConversationData(res.data[0]);
|
|
88
|
+
updateConversationList(res.data, "filter");
|
|
89
|
+
}
|
|
90
|
+
});
|
|
85
91
|
}
|
|
86
92
|
}, [searchParams, conversationList]);
|
|
87
93
|
useEffect(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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;
|
|
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;AA6DD,QAAA,MAAM,oBAAoB,GAAI,uEAO3B,yBAAyB,mDA2M3B,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
|
|
@@ -3,19 +3,29 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffect, useState } from "react";
|
|
4
4
|
import { ConfigProvider, Modal, Switch, message } from "antd";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
|
+
import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
|
|
6
7
|
import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
8
|
+
import { useGroupMembers } from "../../hooks/group/useGroupMembers";
|
|
7
9
|
import { useSetGroupMemberPermissions } from "../../hooks/group/useGroupMemberActions";
|
|
8
10
|
import { Icon } from "../../components/icon";
|
|
9
11
|
import { PERMISSION_SWITCH_COLOR } from "../../constants";
|
|
10
12
|
import { GROUP_PERMISSION_KEY } from "../../constants/im";
|
|
11
|
-
|
|
13
|
+
// "Cập nhật thông tin nhóm" is one switch in the UI but bundles 3 BE
|
|
14
|
+
// permission keys (name/avatar/description) — DROPPII-30533 fix: reading
|
|
15
|
+
// only checked UPDATE_GROUP_NAME, so turning the switch off only ever
|
|
16
|
+
// stripped that one key, leaving avatar/description at the role default.
|
|
17
|
+
const UPDATE_GROUP_INFO_KEYS = [
|
|
18
|
+
GROUP_PERMISSION_KEY.updateGroupName,
|
|
19
|
+
GROUP_PERMISSION_KEY.updateGroupAvatar,
|
|
20
|
+
GROUP_PERMISSION_KEY.updateGroupDescription,
|
|
21
|
+
];
|
|
12
22
|
const permissionListToOverrides = (permissionList) => ({
|
|
13
23
|
sendMessage: permissionList.includes(GROUP_PERMISSION_KEY.sendMessage),
|
|
14
24
|
pinMessage: permissionList.includes(GROUP_PERMISSION_KEY.pinMessage),
|
|
15
25
|
viewMemberInformation: permissionList.includes(GROUP_PERMISSION_KEY.viewMemberInformation),
|
|
16
26
|
addMember: permissionList.includes(GROUP_PERMISSION_KEY.addMember),
|
|
17
27
|
removeMember: permissionList.includes(GROUP_PERMISSION_KEY.removeMember),
|
|
18
|
-
updateGroupInfo: permissionList.includes(
|
|
28
|
+
updateGroupInfo: UPDATE_GROUP_INFO_KEYS.every((key) => permissionList.includes(key)),
|
|
19
29
|
});
|
|
20
30
|
const overridesToPermissionList = (overrides, currentPermissionList) => {
|
|
21
31
|
const desired = {
|
|
@@ -25,6 +35,8 @@ const overridesToPermissionList = (overrides, currentPermissionList) => {
|
|
|
25
35
|
[GROUP_PERMISSION_KEY.addMember]: overrides.addMember,
|
|
26
36
|
[GROUP_PERMISSION_KEY.removeMember]: overrides.removeMember,
|
|
27
37
|
[GROUP_PERMISSION_KEY.updateGroupName]: overrides.updateGroupInfo,
|
|
38
|
+
[GROUP_PERMISSION_KEY.updateGroupAvatar]: overrides.updateGroupInfo,
|
|
39
|
+
[GROUP_PERMISSION_KEY.updateGroupDescription]: overrides.updateGroupInfo,
|
|
28
40
|
};
|
|
29
41
|
const managedKeys = new Set(Object.keys(desired));
|
|
30
42
|
const untouched = currentPermissionList.filter((permission) => !managedKeys.has(permission));
|
|
@@ -33,19 +45,23 @@ const overridesToPermissionList = (overrides, currentPermissionList) => {
|
|
|
33
45
|
.map(([permission]) => permission);
|
|
34
46
|
return [...untouched, ...enabled];
|
|
35
47
|
};
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
48
|
+
// "Configure Admin Permissions" (DROPPII-30248 follow-up). When `member` is
|
|
49
|
+
// passed (from the member list's per-row menu), it patches that one
|
|
50
|
+
// member's permission list via setGroupMemberInfo. When used group-wide (no
|
|
51
|
+
// `member`, e.g. from GroupThreadInfo's group settings), "Lưu" applies the
|
|
52
|
+
// same permission set to every current Admin in the group (DROPPII-30529/
|
|
53
|
+
// 30531 fix) — there is no separate BE endpoint for a role-wide default, so
|
|
54
|
+
// this fans out the existing per-member call to each current Admin instead
|
|
55
|
+
// of only writing to a local override store.
|
|
43
56
|
const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member, onMemberPermissionsSaved, }) => {
|
|
44
57
|
const { t } = useTranslation();
|
|
45
58
|
const groupPermissions = useGroupPermission(groupID);
|
|
46
|
-
const
|
|
59
|
+
const { members: groupMembers } = useGroupMembers(member ? undefined : groupID);
|
|
47
60
|
const { mutateAsync: setMemberPermissions } = useSetGroupMemberPermissions();
|
|
48
61
|
const [isSaving, setIsSaving] = useState(false);
|
|
62
|
+
const isGroupInfoEditable = groupPermissions.updateGroupName &&
|
|
63
|
+
groupPermissions.updateGroupAvatar &&
|
|
64
|
+
groupPermissions.updateGroupDescription;
|
|
49
65
|
const initialOverrides = () => member
|
|
50
66
|
? permissionListToOverrides(member.permissions)
|
|
51
67
|
: {
|
|
@@ -54,7 +70,7 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member,
|
|
|
54
70
|
viewMemberInformation: groupPermissions.viewMemberInformation,
|
|
55
71
|
addMember: groupPermissions.addMember,
|
|
56
72
|
removeMember: groupPermissions.removeMember,
|
|
57
|
-
updateGroupInfo:
|
|
73
|
+
updateGroupInfo: isGroupInfoEditable,
|
|
58
74
|
};
|
|
59
75
|
const [pending, setPending] = useState(initialOverrides);
|
|
60
76
|
useEffect(() => {
|
|
@@ -71,7 +87,7 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member,
|
|
|
71
87
|
viewMemberInformation: groupPermissions.viewMemberInformation,
|
|
72
88
|
addMember: groupPermissions.addMember,
|
|
73
89
|
removeMember: groupPermissions.removeMember,
|
|
74
|
-
updateGroupInfo:
|
|
90
|
+
updateGroupInfo: isGroupInfoEditable,
|
|
75
91
|
};
|
|
76
92
|
const hasChanges = pending.sendMessage !== baseline.sendMessage ||
|
|
77
93
|
pending.pinMessage !== baseline.pinMessage ||
|
|
@@ -95,7 +111,15 @@ const AdminPermissionModal = ({ open, groupID, onClose, inline = false, member,
|
|
|
95
111
|
onMemberPermissionsSaved === null || onMemberPermissionsSaved === void 0 ? void 0 : onMemberPermissionsSaved(member.userID, nextPermissions);
|
|
96
112
|
}
|
|
97
113
|
else {
|
|
98
|
-
|
|
114
|
+
const admins = groupMembers.filter((groupMember) => groupMember.roleLevel === GroupMemberRole.Admin);
|
|
115
|
+
await Promise.all(admins.map((admin) => {
|
|
116
|
+
var _a;
|
|
117
|
+
return setMemberPermissions({
|
|
118
|
+
groupID,
|
|
119
|
+
userID: admin.userID,
|
|
120
|
+
permissions: overridesToPermissionList(pending, (_a = admin.permissions) !== null && _a !== void 0 ? _a : []),
|
|
121
|
+
});
|
|
122
|
+
}));
|
|
99
123
|
}
|
|
100
124
|
message.success(t("update_admin_permissions_success"));
|
|
101
125
|
onClose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GroupMembersDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/message/GroupMembersDrawer.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAiB/C,UAAU,uBAAuB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA0CD,QAAA,MAAM,kBAAkB,GAAI,wDAKzB,uBAAuB,4CA4iBzB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { GroupMemberRole } from "@droppii-org/wasm-client-sdk";
|
|
3
|
-
import { Avatar, Button, Drawer, Dropdown, Empty, Input, message,
|
|
3
|
+
import { Avatar, Button, Drawer, Dropdown, Empty, Input, message, Spin } from "antd";
|
|
4
|
+
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
4
5
|
import { useEffect, useMemo, useState } from "react";
|
|
5
6
|
import { useDebounce } from "ahooks";
|
|
6
7
|
import { useTranslation } from "react-i18next";
|
|
@@ -202,6 +203,16 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
202
203
|
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"));
|
|
203
204
|
}
|
|
204
205
|
};
|
|
206
|
+
const handleConfirmAssignAdmin = async () => {
|
|
207
|
+
if ((confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) !== "assign-admin")
|
|
208
|
+
return;
|
|
209
|
+
try {
|
|
210
|
+
await handleToggleAdminRole(confirmMemberAction.member);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
setConfirmMemberAction(null);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
205
216
|
const handleConfirmTransferOwner = async () => {
|
|
206
217
|
var _a, _b;
|
|
207
218
|
if (!groupID || (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) !== "transfer-owner")
|
|
@@ -256,7 +267,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
256
267
|
const leaveGroupItem = {
|
|
257
268
|
key: "leave-group",
|
|
258
269
|
label: t("leave_group"),
|
|
259
|
-
icon: _jsx(Icon, { icon: "logout-
|
|
270
|
+
icon: _jsx(Icon, { icon: "logout-o", size: 16 }),
|
|
260
271
|
onClick: handleLeaveGroup,
|
|
261
272
|
danger: true,
|
|
262
273
|
};
|
|
@@ -266,7 +277,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
266
277
|
{
|
|
267
278
|
key: "mute",
|
|
268
279
|
label: t("mute_member"),
|
|
269
|
-
icon: _jsx(Icon, { icon: "bell-off-
|
|
280
|
+
icon: _jsx(Icon, { icon: "bell-off-o", size: 16 }),
|
|
270
281
|
disabled: true,
|
|
271
282
|
},
|
|
272
283
|
leaveGroupItem,
|
|
@@ -276,7 +287,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
276
287
|
{
|
|
277
288
|
key: "send-message",
|
|
278
289
|
label: t("send_message"),
|
|
279
|
-
icon: _jsx(Icon, { icon: "send-
|
|
290
|
+
icon: _jsx(Icon, { icon: "send-o", size: 16 }),
|
|
280
291
|
disabled: true,
|
|
281
292
|
},
|
|
282
293
|
{
|
|
@@ -284,14 +295,14 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
284
295
|
label: member.isSendMessageDisabled
|
|
285
296
|
? t("unmute_member")
|
|
286
297
|
: t("mute_member"),
|
|
287
|
-
icon: _jsx(Icon, { icon: "bell-off-
|
|
298
|
+
icon: _jsx(Icon, { icon: "bell-off-o", size: 16 }),
|
|
288
299
|
disabled: !permissions.removeMember,
|
|
289
300
|
onClick: () => void handleToggleMemberSendMessage(member),
|
|
290
301
|
},
|
|
291
302
|
{
|
|
292
303
|
key: "transfer-owner",
|
|
293
304
|
label: t("transfer_owner"),
|
|
294
|
-
icon: _jsx(Icon, { icon: "key-
|
|
305
|
+
icon: _jsx(Icon, { icon: "key-o", size: 16 }),
|
|
295
306
|
disabled: !permissions.transferOwnership,
|
|
296
307
|
onClick: () => setConfirmMemberAction({ type: "transfer-owner", member }),
|
|
297
308
|
},
|
|
@@ -307,7 +318,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
307
318
|
{
|
|
308
319
|
key: "remove-admin",
|
|
309
320
|
label: t("remove_from_admin"),
|
|
310
|
-
icon: _jsx(Icon, { icon: "shield-fail-
|
|
321
|
+
icon: _jsx(Icon, { icon: "shield-fail-o", size: 16 }),
|
|
311
322
|
disabled: !permissions.addAdmin,
|
|
312
323
|
onClick: () => void handleToggleAdminRole(member),
|
|
313
324
|
},
|
|
@@ -316,15 +327,15 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
316
327
|
{
|
|
317
328
|
key: "assign-admin",
|
|
318
329
|
label: t("assign_as_admin"),
|
|
319
|
-
icon: _jsx(Icon, { icon: "shield-
|
|
330
|
+
icon: _jsx(Icon, { icon: "shield-o", size: 16 }),
|
|
320
331
|
disabled: !permissions.addAdmin,
|
|
321
|
-
onClick: () =>
|
|
332
|
+
onClick: () => setConfirmMemberAction({ type: "assign-admin", member }),
|
|
322
333
|
},
|
|
323
334
|
]),
|
|
324
335
|
{
|
|
325
336
|
key: "remove-member",
|
|
326
337
|
label: t("remove_from_group"),
|
|
327
|
-
icon: _jsx(Icon, { icon: "user-del-
|
|
338
|
+
icon: _jsx(Icon, { icon: "user-del-o", size: 16 }),
|
|
328
339
|
danger: true,
|
|
329
340
|
disabled: !permissions.removeMember,
|
|
330
341
|
onClick: () => setConfirmMemberAction({ type: "remove", member }),
|
|
@@ -332,7 +343,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
332
343
|
{
|
|
333
344
|
key: "remove-and-block",
|
|
334
345
|
label: t("remove_and_block"),
|
|
335
|
-
icon: _jsx(Icon, { icon: "user-octagon-
|
|
346
|
+
icon: _jsx(Icon, { icon: "user-octagon-o", size: 16 }),
|
|
336
347
|
danger: true,
|
|
337
348
|
disabled: true,
|
|
338
349
|
},
|
|
@@ -340,7 +351,7 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
340
351
|
return (_jsxs("div", { className: "flex items-center gap-3 px-4 py-2", children: [_jsx(Avatar, { src: member.faceURL, size: 40, className: "shrink-0", children: member.nickname.charAt(0) || "A" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("p", { className: "text-sm font-semibold text-gray-900 truncate", children: member.nickname }), member.isCurrentUser && (_jsx("span", { className: "text-xs text-blue-500 shrink-0", children: t("current_account") }))] }), member.roleLabel && (_jsx("p", { className: "text-sm text-gray-500 truncate", children: member.roleLabel }))] }), (isRealGroup || member.isCurrentUser) && (_jsx(Dropdown, { menu: { items: menuItems }, trigger: ["click"], placement: "bottomRight", children: _jsx("button", { type: "button", className: "flex items-center justify-center w-8 h-8 rounded-full text-gray-500 hover:bg-gray-100", onClick: (event) => event.stopPropagation(), children: _jsx(Icon, { icon: "more-horizontal-b", size: 18 }) }) }))] }, member.userID));
|
|
341
352
|
}) }));
|
|
342
353
|
};
|
|
343
|
-
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: handleOpenDrawer, disabled: disabled || !groupID, children: _jsx(Icon, { icon: "user-two-check-
|
|
354
|
+
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: handleOpenDrawer, disabled: disabled || !groupID, children: _jsx(Icon, { icon: "user-two-check-o", size: 22 }) }), _jsx(Drawer, { open: isOpen, onClose: handleCloseDrawer, mask: false, closeIcon: false, styles: {
|
|
344
355
|
body: {
|
|
345
356
|
padding: 0,
|
|
346
357
|
height: "100%",
|
|
@@ -350,9 +361,11 @@ const GroupMembersDrawer = ({ groupID, conversationId, currentSession, disabled
|
|
|
350
361
|
userID: adminPermissionMember.userID,
|
|
351
362
|
permissions: adminPermissionMember.permissions,
|
|
352
363
|
}
|
|
353
|
-
: undefined, onMemberPermissionsSaved: patchMemberPermissions })), _jsx(
|
|
364
|
+
: undefined, onMemberPermissionsSaved: patchMemberPermissions })), _jsx(ConfirmModal, { open: showLeaveConfirm, onOk: handleConfirmLeave, onCancel: () => setShowLeaveConfirm(false), title: t("leave_group_confirm_title"), okText: t("leave_group"), cancelText: t("cancel"), okType: "danger", confirmLoading: isLeaving, children: _jsx("p", { children: t("leave_group_confirm_message") }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "remove", onOk: handleConfirmRemoveMember, onCancel: () => setConfirmMemberAction(null), title: t("remove_member_confirm_title"), okText: t("remove_from_group"), cancelText: t("cancel"), okType: "danger", children: _jsx("p", { children: t("remove_member_confirm_message", {
|
|
365
|
+
name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
|
|
366
|
+
}) }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "transfer-owner", onOk: handleConfirmTransferOwner, onCancel: () => setConfirmMemberAction(null), title: t("transfer_owner_confirm_title"), okText: t("transfer_owner"), cancelText: t("cancel"), okType: "danger", children: _jsx("p", { children: t("transfer_owner_confirm_message", {
|
|
354
367
|
name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
|
|
355
|
-
}) }) }), _jsx(
|
|
368
|
+
}) }) }), _jsx(ConfirmModal, { open: (confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.type) === "assign-admin", onOk: handleConfirmAssignAdmin, onCancel: () => setConfirmMemberAction(null), title: t("assign_admin_confirm_title"), okText: t("agree"), cancelText: t("cancel"), children: _jsx("p", { children: t("assign_admin_confirm_message", {
|
|
356
369
|
name: confirmMemberAction === null || confirmMemberAction === void 0 ? void 0 : confirmMemberAction.member.nickname,
|
|
357
370
|
}) }) })] }));
|
|
358
371
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/message/MessageList.tsx"],"names":[],"mappings":"AAgCA,UAAU,gBAAgB;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,WAAW,GAAI,OAAO,gBAAgB,4CAgS3C,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMessage } from "../../hooks/message/useMessage";
|
|
4
|
-
import { Button, Empty,
|
|
4
|
+
import { Button, Empty, Spin, message as toastMessage } from "antd";
|
|
5
5
|
import { useCallback, useEffect, useState } from "react";
|
|
6
6
|
import dayjs from "dayjs";
|
|
7
7
|
import isToday from "dayjs/plugin/isToday";
|
|
8
|
+
import { GroupMemberRole, GroupStatus } from "@droppii-org/wasm-client-sdk";
|
|
8
9
|
import emitter from "../../utils/events";
|
|
9
10
|
import MessageItem from "./item";
|
|
10
11
|
import MessageInfiniteScroll from "./MessageInfiniteScroll";
|
|
@@ -24,6 +25,7 @@ import { useGroupPermission } from "../../hooks/group/useGroupPermission";
|
|
|
24
25
|
import { isGroupSession } from "../../utils/imCommon";
|
|
25
26
|
import { ConnectStatus, SessionStatus } from "../../types/chat";
|
|
26
27
|
import PinLimitModal from "../../components/conversation-detail/PinLimitModal";
|
|
28
|
+
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
27
29
|
dayjs.extend(isToday);
|
|
28
30
|
const MessageList = (props) => {
|
|
29
31
|
const { t } = useTranslation();
|
|
@@ -32,7 +34,19 @@ const MessageList = (props) => {
|
|
|
32
34
|
const conversationData = useConversationStore((state) => state.conversationData);
|
|
33
35
|
const permissions = useGroupPermission(conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID);
|
|
34
36
|
const isGroupChat = isGroupSession(conversationData === null || conversationData === void 0 ? void 0 : conversationData.conversationType);
|
|
35
|
-
const
|
|
37
|
+
const currentGroupInfo = useConversationStore((state) => state.currentGroupInfo);
|
|
38
|
+
const currentMemberInGroup = useConversationStore((state) => state.currentMemberInGroup);
|
|
39
|
+
// Group/member data refetches async on conversation switch and can briefly
|
|
40
|
+
// hold the previous conversation's groupID or a not-yet-loaded roleLevel.
|
|
41
|
+
const isGroupStateFresh = !isGroupChat ||
|
|
42
|
+
((currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.groupID) === (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) &&
|
|
43
|
+
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.groupID) === (conversationData === null || conversationData === void 0 ? void 0 : conversationData.groupID) &&
|
|
44
|
+
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) !== undefined);
|
|
45
|
+
// Group-wide mute (GroupThreadInfo.tsx) restricts Normal members only.
|
|
46
|
+
const isRestrictedByGroupMute = isGroupChat &&
|
|
47
|
+
(currentGroupInfo === null || currentGroupInfo === void 0 ? void 0 : currentGroupInfo.status) === GroupStatus.Muted &&
|
|
48
|
+
(currentMemberInGroup === null || currentMemberInGroup === void 0 ? void 0 : currentMemberInGroup.roleLevel) === GroupMemberRole.Normal;
|
|
49
|
+
const canSendMessage = !isGroupChat || (permissions.sendMessage && !isRestrictedByGroupMute);
|
|
36
50
|
const canPinMessage = !isGroupChat || permissions.pinMessage;
|
|
37
51
|
const setQuotedMessage = useConversationStore((state) => state.setQuotedMessage);
|
|
38
52
|
const setSearchClientMsgID = useConversationStore((state) => state.setSearchClientMsgID);
|
|
@@ -136,6 +150,6 @@ const MessageList = (props) => {
|
|
|
136
150
|
if (!conversationData) {
|
|
137
151
|
return (_jsx("div", { className: "flex flex-1 items-center justify-center h-full", children: connectStatus !== ConnectStatus.Connected ? (_jsx(Spin, {})) : (_jsx(Empty, { description: t("no_conversation_data") })) }));
|
|
138
152
|
}
|
|
139
|
-
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t("send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(
|
|
153
|
+
return (_jsxs("div", { className: "flex flex-col flex-1 relative h-full min-w-0 bg-white", style: { overflowX: "hidden" }, children: [_jsx(MessageHeader, { onClose: onClose, currentSession: latestConversationSession, isJoined: isJoined, onOpenThreadInfo: onOpenThreadInfo }), _jsx(MessageInfiniteScroll, { scrollRef: scrollRef, scrollableTargetId: "scrollableMessagesDiv", loadState: loadState, moreOldLoading: moreOldLoading, onLoadMoreOld: loadMoreOldMessage, onScroll: handleInfiniteScroll, children: loadState.messageList.map((message, _, array) => (_jsx(MessageItem, { message: message, allMessages: array, contextMenuOpen: openMenuId === message.clientMsgID, onContextMenuOpenChange: (open) => setOpenMenuId(open ? message.clientMsgID : null), onRevokeMessage: handleOpenRevoke, onQuoteMessage: setQuotedMessage, onPressQuoteMessage: onPressQuoteMessage, onPinMessage: canPinMessage ? handlePinMessage : undefined, onUnpinMessage: canPinMessage ? handleUnpinMessage : undefined }, message.clientMsgID))) }), moreNewLoading && (_jsx("div", { className: "flex items-center justify-center py-2", children: _jsx(Spin, {}) })), isCheckingMembership || !isGroupStateFresh ? (_jsx("div", { className: "border-t bg-white py-4 flex items-center justify-center", children: _jsx(Spin, {}) })) : (latestConversationSession === null || latestConversationSession === void 0 ? void 0 : latestConversationSession.status) === SessionStatus.UNASSIGNED ? (_jsx(UnassignedSessionFooter, { sessionId: latestConversationSession.id, teamId: latestConversationSession.teamId })) : shouldRestrictToLastSession ? (_jsxs("div", { className: "border-t bg-white py-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-gray-500", children: t("join_group_required") }), _jsx(Button, { type: "primary", onClick: joinGroup, loading: isJoining, children: t("join_group") })] })) : !canSendMessage ? (_jsx("div", { className: "border-t bg-[#f5f5f5] py-3 flex items-center justify-center", children: _jsx("p", { className: "text-sm text-gray-500", children: t(isRestrictedByGroupMute ? "send_message_disabled_group_mute" : "send_message_disabled") }) })) : (_jsx(MessageFooter, { currentSession: latestConversationSession, openCreateCannedModal: openCreateCannedModal })), _jsx(ConfirmModal, { open: showConfirmRevoke, onOk: onRevokeMessage, onCancel: handleCloseRevoke, title: t("revoke_message_confirm_title"), okText: t("revoke"), cancelText: t("cancel"), okType: "danger", confirmLoading: isRevoking, children: _jsx("p", { children: t("revoke_message_confirm_message") }) }), _jsx(PinLimitModal, { open: pinLimitModalOpen, max: pinnedTotalCount, onClose: () => setPinLimitModalOpen(false) })] }));
|
|
140
154
|
};
|
|
141
155
|
export default MessageList;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
2
|
export interface MessageBubbleProps {
|
|
3
3
|
isMine: boolean;
|
|
4
4
|
content: ReactNode;
|
|
@@ -8,7 +8,8 @@ export interface MessageBubbleProps {
|
|
|
8
8
|
isInternal?: boolean;
|
|
9
9
|
id?: string;
|
|
10
10
|
isTransparent?: boolean;
|
|
11
|
+
timeLabel?: string;
|
|
11
12
|
}
|
|
12
|
-
declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare const MessageBubble: ({ isMine, content, senderName, showSenderName, showTip, isInternal, id, isTransparent, timeLabel, }: MessageBubbleProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
14
|
export default MessageBubble;
|
|
14
15
|
//# sourceMappingURL=MessageBubble.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/MessageBubble.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4CD,QAAA,MAAM,aAAa,GAAI,qGAUpB,kBAAkB,4CAiEpB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import clsx from "clsx";
|
|
4
|
+
import { useState } from "react";
|
|
4
5
|
const INCOMING_TAIL_PATH = "M1.3798 18H4V0C3.51762 4.5 3.34269 10.5 0.966518 14C-0.568384 16.2608 -0.159364 18 1.3798 18Z";
|
|
5
6
|
const OUTGOING_TAIL_PATH = "M2.6202 18H0V0C0.482375 4.5 0.657308 10.5 3.03348 14C4.56838 16.2608 4.15936 18 2.6202 18Z";
|
|
6
7
|
const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className: clsx("flex shrink-0 items-center justify-center", isInternal
|
|
@@ -8,8 +9,10 @@ const MessageBubbleTail = ({ isMine, isInternal, }) => (_jsx("div", { className:
|
|
|
8
9
|
: isMine
|
|
9
10
|
? "text-chat-bubble-mine"
|
|
10
11
|
: "text-chat-bubble-neutral"), children: _jsx("div", { className: "flex-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "4", height: "18", viewBox: "0 0 4 18", fill: "none", "aria-hidden": true, children: _jsx("path", { d: isMine ? OUTGOING_TAIL_PATH : INCOMING_TAIL_PATH, fill: "currentColor", fillOpacity: "0.1" }) }) }) }));
|
|
11
|
-
const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, }) => {
|
|
12
|
+
const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip = true, isInternal = false, id, isTransparent = false, timeLabel, }) => {
|
|
12
13
|
const showTail = showTip && !isTransparent;
|
|
14
|
+
const [timeRevealed, setTimeRevealed] = useState(false);
|
|
15
|
+
const showTimeLabel = Boolean(timeLabel) && (showTip || timeRevealed);
|
|
13
16
|
const bubbleClass = clsx("relative max-w-full", isTransparent
|
|
14
17
|
? "bg-transparent p-0"
|
|
15
18
|
: clsx("p-2", showTip
|
|
@@ -21,6 +24,9 @@ const MessageBubble = ({ isMine, content, senderName, showSenderName, showTip =
|
|
|
21
24
|
: isMine
|
|
22
25
|
? "bg-chat-bubble-mine/10"
|
|
23
26
|
: "bg-chat-bubble-neutral/10"));
|
|
24
|
-
return (_jsxs("div", { className: "relative flex items-end", "data-testid": "message-bubble",
|
|
27
|
+
return (_jsxs("div", { className: "relative flex items-end", "data-testid": "message-bubble", onClick: () => {
|
|
28
|
+
if (!showTip && timeLabel)
|
|
29
|
+
setTimeRevealed((prev) => !prev);
|
|
30
|
+
}, children: [!isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), _jsx("div", { className: bubbleClass, id: id, children: _jsxs("div", { className: clsx("flex flex-col gap-1", !isTransparent && "px-1"), children: [showSenderName && senderName && (_jsx("span", { className: "text-[12px] font-medium leading-[160%] tracking-[0.12px] text-gray-400", children: senderName })), _jsx("div", { className: "flex flex-col gap-2", children: content })] }) }), isMine && showTail && (_jsx(MessageBubbleTail, { isMine: isMine, isInternal: isInternal })), showTimeLabel && (_jsx("span", { "data-testid": "message-time-label", className: clsx("absolute -bottom-2 flex h-[19px] w-[30px] items-center justify-center rounded-full bg-white text-[10px] text-gray-400 shadow-sm", isMine ? "right-1" : "left-1"), children: timeLabel }))] }));
|
|
25
31
|
};
|
|
26
32
|
export default MessageBubble;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/message/item/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,IAAI,eAAe,EAG/B,MAAM,8BAA8B,CAAC;AAmCtC,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAyBD,QAAA,MAAM,WAAW,GAAI,qMAalB,gBAAgB,mDA6SlB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -8,7 +8,8 @@ import MessageBubble from "./MessageBubble";
|
|
|
8
8
|
import { renderBubbleContent } from "./renderMessageContent";
|
|
9
9
|
import { getVisibleNeighbor, isMessageHiddenForViewer, } from "../../../hooks/message/useMessage";
|
|
10
10
|
import { MSG_ITEM_CONTENT_PREFIX, MSG_ITEM_PREFIX } from "../../../constants";
|
|
11
|
-
import {
|
|
11
|
+
import { formatDividerTimestamp } from "../../../utils/common";
|
|
12
|
+
import { CustomMessageType } from "../../../types/customMessage";
|
|
12
13
|
import useAuthStore from "../../../store/auth";
|
|
13
14
|
import useConversationStore from "../../../store/conversation";
|
|
14
15
|
import { useMemo, useRef } from "react";
|
|
@@ -22,6 +23,9 @@ import GroupNotificationMessageItem from "./GroupNotificationMessage";
|
|
|
22
23
|
import { isGroupNotificationMessageType, isSystemLogMessageType } from "../../../utils/messageLog";
|
|
23
24
|
import { buildMessageToolboxMenuItems, getCopyableMessageText, isCopyableMessageType, isPinnableMessageType, MessageToolboxActionKey, MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, useMessageToolboxVisibility, } from "../../../components/message/toolbox";
|
|
24
25
|
const MESSAGE_AVATAR_SIZE = 24;
|
|
26
|
+
const DIVIDER_GAP_MINUTES = 30;
|
|
27
|
+
const GROUPING_GAP_MINUTES = 5;
|
|
28
|
+
const isOrderMessage = (message) => { var _a; return ((_a = message === null || message === void 0 ? void 0 : message.customElem) === null || _a === void 0 ? void 0 : _a.extension) === CustomMessageType.Order; };
|
|
25
29
|
const MessageAvatar = ({ avatarUrl, senderNickname, }) => {
|
|
26
30
|
var _a;
|
|
27
31
|
return (_jsx(Avatar, { size: MESSAGE_AVATAR_SIZE, src: avatarUrl, className: "shrink-0 self-end", children: ((_a = senderNickname === null || senderNickname === void 0 ? void 0 : senderNickname.charAt) === null || _a === void 0 ? void 0 : _a.call(senderNickname, 0)) || "A" }));
|
|
@@ -127,10 +131,21 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
127
131
|
message.clientMsgID;
|
|
128
132
|
const previousMessage = getVisibleNeighbor(allMessages, message, "prev");
|
|
129
133
|
const nextMessage = getVisibleNeighbor(allMessages, message, "next");
|
|
130
|
-
const
|
|
131
|
-
const
|
|
134
|
+
const isOrder = isOrderMessage(message);
|
|
135
|
+
const prevSameUser = !isOrder &&
|
|
136
|
+
!isOrderMessage(previousMessage) &&
|
|
137
|
+
(previousMessage === null || previousMessage === void 0 ? void 0 : previousMessage.sendID) === (message === null || message === void 0 ? void 0 : message.sendID) &&
|
|
138
|
+
Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "minute")) <=
|
|
139
|
+
GROUPING_GAP_MINUTES;
|
|
140
|
+
const nextSameUser = !isOrder &&
|
|
141
|
+
!isOrderMessage(nextMessage) &&
|
|
142
|
+
(nextMessage === null || nextMessage === void 0 ? void 0 : nextMessage.sendID) === (message === null || message === void 0 ? void 0 : message.sendID) &&
|
|
143
|
+
Math.abs(dayjs(nextMessage.sendTime).diff(message.sendTime, "minute")) <=
|
|
144
|
+
GROUPING_GAP_MINUTES;
|
|
132
145
|
const showTimeBreak = !previousMessage ||
|
|
133
|
-
!dayjs(message.sendTime).isSame(previousMessage.sendTime, "days")
|
|
146
|
+
!dayjs(message.sendTime).isSame(previousMessage.sendTime, "days") ||
|
|
147
|
+
Math.abs(dayjs(message.sendTime).diff(previousMessage.sendTime, "minute")) >
|
|
148
|
+
DIVIDER_GAP_MINUTES;
|
|
134
149
|
const showPeerInfo = isGroupChat || showSenderInfo;
|
|
135
150
|
const showAvatar = showPeerInfo && !isMine && !nextSameUser;
|
|
136
151
|
const showSenderName = showPeerInfo && !isMine && !prevSameUser;
|
|
@@ -142,7 +157,7 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
142
157
|
className: "flex flex-col gap-2 py-1 px-3 sm:px-4 min-w-0",
|
|
143
158
|
id: `${MSG_ITEM_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`,
|
|
144
159
|
};
|
|
145
|
-
const timeBreakNode = showTimeBreak ? (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "rounded-full bg-neutral-100 px-2 py-1 text-center text-xs text-gray-600", children:
|
|
160
|
+
const timeBreakNode = showTimeBreak ? (_jsx("div", { className: "flex justify-center", children: _jsx("span", { className: "rounded-full bg-neutral-100 px-2 py-1 text-center text-xs text-gray-600", children: formatDividerTimestamp(message.sendTime) }) })) : null;
|
|
146
161
|
if (isSystemLogMessage) {
|
|
147
162
|
return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
|
|
148
163
|
timeBreakNode,
|
|
@@ -153,8 +168,8 @@ const MessageItem = ({ message, allMessages, isGroupChat = false, forceHideTip =
|
|
|
153
168
|
timeBreakNode,
|
|
154
169
|
_jsx(GroupNotificationMessageItem, { message: message })));
|
|
155
170
|
}
|
|
156
|
-
const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: renderBubbleContent(message, onPressQuoteMessage), isTransparent: isStickerMessage }));
|
|
157
|
-
const showPinnedBadge = Boolean(message === null || message === void 0 ? void 0 : message.isPinned)
|
|
171
|
+
const bubbleNode = (_jsx(MessageBubble, { isMine: isMine, isInternal: isInternalMessage, senderName: message === null || message === void 0 ? void 0 : message.senderNickname, showSenderName: showSenderName, showTip: showTip, id: `${MSG_ITEM_CONTENT_PREFIX}${message === null || message === void 0 ? void 0 : message.clientMsgID}`, content: renderBubbleContent(message, onPressQuoteMessage), isTransparent: isStickerMessage, timeLabel: dayjs.tz(message.sendTime).format("HH:mm") }));
|
|
172
|
+
const showPinnedBadge = Boolean(message === null || message === void 0 ? void 0 : message.isPinned);
|
|
158
173
|
const pinnedBadgeNode = showPinnedBadge ? (_jsx("span", { "data-testid": "message-pinned-badge", className: clsx("absolute -top-2.5 z-[1] flex h-[22px] w-[26px] items-center justify-center rounded-full border border-white bg-gray-25", isMine ? "left-0" : "right-0"), children: _jsx(Icon, { icon: "thumbtack-b", size: 12, className: "text-primary-400" }) })) : null;
|
|
159
174
|
const wrappedBubble = hasContextMenu ? (_jsx(Dropdown, { menu: { items: contextMenuItems }, trigger: ["contextMenu"], open: contextMenuOpen, onOpenChange: onContextMenuOpenChange, overlayClassName: MESSAGE_TOOLBOX_OVERLAY_CLASSNAME, children: _jsxs("div", { ref: bubbleWrapperRef, className: "relative min-w-0", children: [bubbleNode, pinnedBadgeNode] }) })) : (_jsxs("div", { className: "relative min-w-0", children: [bubbleNode, pinnedBadgeNode] }));
|
|
160
175
|
return (_createElement("div", Object.assign({}, messageContainerProps, { key: message === null || message === void 0 ? void 0 : message.clientMsgID }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GroupThreadInfo.d.ts","sourceRoot":"","sources":["../../../src/components/thread/GroupThreadInfo.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAID,QAAA,MAAM,eAAe,GAAI,qBAAqB,oBAAoB,mDAyjBjE,CAAC;AAEF,eAAe,eAAe,CAAC"}
|