@ermis-network/ermis-chat-react 1.0.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -0
- package/dist/index.cjs +8320 -3427
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1277 -291
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +1131 -99
- package/dist/index.d.ts +1131 -99
- package/dist/index.mjs +8168 -3319
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
- package/src/channelTypeUtils.ts +1 -1
- package/src/components/Avatar.tsx +2 -1
- package/src/components/Channel.tsx +6 -5
- package/src/components/ChannelActions.tsx +67 -3
- package/src/components/ChannelHeader.tsx +27 -37
- package/src/components/ChannelInfo/AddMemberModal.tsx +12 -2
- package/src/components/ChannelInfo/ChannelInfo.tsx +410 -187
- package/src/components/ChannelInfo/ChannelInfoTabs.tsx +59 -297
- package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +30 -174
- package/src/components/ChannelInfo/EditChannelModal.tsx +6 -3
- package/src/components/ChannelInfo/MediaGridItem.tsx +215 -68
- package/src/components/ChannelInfo/MemberListItem.tsx +2 -3
- package/src/components/ChannelInfo/MessageSearchPanel.tsx +27 -126
- package/src/components/ChannelInfo/States.tsx +1 -1
- package/src/components/ChannelInfo/index.ts +3 -0
- package/src/components/ChannelInfo/useChannelInfoTabs.tsx +427 -0
- package/src/components/ChannelInfo/useChannelSettings.ts +212 -0
- package/src/components/ChannelInfo/useMessageSearch.tsx +141 -0
- package/src/components/ChannelList.tsx +247 -301
- package/src/components/CreateChannelModal.tsx +290 -93
- package/src/components/Dropdown.tsx +1 -16
- package/src/components/EditPreview.tsx +1 -0
- package/src/components/ErmisCallProvider.tsx +72 -17
- package/src/components/ErmisCallUI.tsx +43 -20
- package/src/components/FilesPreview.tsx +8 -12
- package/src/components/FlatTopicGroupItem.tsx +243 -0
- package/src/components/ForwardMessageModal.tsx +43 -81
- package/src/components/MediaLightbox.tsx +454 -292
- package/src/components/MentionSuggestions.tsx +47 -35
- package/src/components/MessageActionsBox.tsx +6 -1
- package/src/components/MessageInput.tsx +165 -17
- package/src/components/MessageInputDefaults.tsx +127 -1
- package/src/components/MessageItem.tsx +155 -43
- package/src/components/MessageQuickReactions.tsx +153 -23
- package/src/components/MessageReactions.tsx +49 -3
- package/src/components/MessageRenderers.tsx +1114 -445
- package/src/components/Panel.tsx +1 -14
- package/src/components/PinnedMessages.tsx +55 -15
- package/src/components/PreviewOverlay.tsx +24 -0
- package/src/components/QuotedMessagePreview.tsx +99 -8
- package/src/components/ReadReceipts.tsx +2 -1
- package/src/components/RecoveryPin/RecoveryPin.tsx +279 -0
- package/src/components/RecoveryPin/index.ts +19 -0
- package/src/components/TopicList.tsx +236 -0
- package/src/components/TopicModal.tsx +4 -1
- package/src/components/TypingIndicator.tsx +17 -8
- package/src/components/UserPicker.tsx +94 -16
- package/src/components/VirtualMessageList.tsx +419 -113
- package/src/context/ChatComponentsContext.tsx +14 -0
- package/src/context/ChatProvider.tsx +44 -14
- package/src/context/ErmisCallContext.tsx +4 -0
- package/src/hooks/useChannelCapabilities.ts +7 -4
- package/src/hooks/useChannelData.ts +10 -3
- package/src/hooks/useChannelListUpdates.ts +94 -21
- package/src/hooks/useChannelMessages.ts +391 -42
- package/src/hooks/useChannelRowUpdates.ts +36 -5
- package/src/hooks/useChatUser.ts +39 -0
- package/src/hooks/useContactChannels.ts +45 -0
- package/src/hooks/useContactCount.ts +50 -0
- package/src/hooks/useDownloadHandler.ts +36 -0
- package/src/hooks/useDragAndDrop.ts +79 -0
- package/src/hooks/useE2eeAttachmentRenderer.ts +204 -0
- package/src/hooks/useE2eeFileUpload.ts +38 -0
- package/src/hooks/useFileUpload.ts +25 -5
- package/src/hooks/useForwardMessage.ts +309 -0
- package/src/hooks/useInviteChannels.ts +88 -0
- package/src/hooks/useInviteCount.ts +104 -0
- package/src/hooks/useLoadMessages.ts +16 -4
- package/src/hooks/useMentions.ts +60 -7
- package/src/hooks/useMessageActions.ts +19 -10
- package/src/hooks/useMessageSend.ts +64 -12
- package/src/hooks/usePendingE2eeSends.ts +29 -0
- package/src/hooks/usePendingState.ts +21 -4
- package/src/hooks/usePreviewState.ts +69 -0
- package/src/hooks/useRecoveryPin.ts +287 -0
- package/src/hooks/useScrollToMessage.ts +29 -4
- package/src/hooks/useStickerPicker.ts +62 -0
- package/src/hooks/useTopicGroupUpdates.ts +235 -0
- package/src/index.ts +79 -6
- package/src/messageTypeUtils.ts +27 -1
- package/src/styles/_base.css +0 -1
- package/src/styles/_call-ui.css +59 -2
- package/src/styles/_channel-info.css +50 -4
- package/src/styles/_channel-list.css +131 -68
- package/src/styles/_create-channel-modal.css +10 -0
- package/src/styles/_forward-modal.css +16 -1
- package/src/styles/_media-lightbox.css +67 -2
- package/src/styles/_mentions.css +1 -1
- package/src/styles/_message-actions.css +3 -4
- package/src/styles/_message-bubble.css +631 -112
- package/src/styles/_message-input.css +139 -0
- package/src/styles/_message-list.css +91 -18
- package/src/styles/_message-quick-reactions.css +105 -32
- package/src/styles/_message-reactions.css +22 -32
- package/src/styles/_modal.css +2 -1
- package/src/styles/_preview-overlay.css +38 -0
- package/src/styles/_recovery-pin.css +97 -0
- package/src/styles/_tokens.css +22 -20
- package/src/styles/_typing-indicator.css +26 -10
- package/src/styles/index.css +2 -0
- package/src/types.ts +477 -15
- package/src/utils/avatarColors.ts +48 -0
- package/src/utils.ts +219 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ermis-network/ermis-chat-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "React UI components for Ermis Chat",
|
|
5
5
|
"author": "Ermis",
|
|
6
6
|
"homepage": "https://ermis.network/",
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
"/src"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@ermis-network/ermis-chat-sdk": "
|
|
24
|
-
"
|
|
23
|
+
"@ermis-network/ermis-chat-sdk": "2.0.1",
|
|
24
|
+
"react-ts-audio-recorder": "^1.1.4",
|
|
25
|
+
"virtua": "^0.48.8",
|
|
26
|
+
"frimousse": "^0.3.0"
|
|
25
27
|
},
|
|
26
28
|
"peerDependencies": {
|
|
27
29
|
"react": ">=18.0.0",
|
|
@@ -30,13 +32,16 @@
|
|
|
30
32
|
"devDependencies": {
|
|
31
33
|
"@types/react": "^18.2.0",
|
|
32
34
|
"@types/react-dom": "^18.2.0",
|
|
35
|
+
"lucide-react": "^0.474.0",
|
|
36
|
+
"motion": "^12.0.0",
|
|
33
37
|
"react": "^18.2.0",
|
|
34
38
|
"react-dom": "^18.2.0",
|
|
39
|
+
"react-virtuoso": "^4.12.5",
|
|
35
40
|
"tsup": "^8.0.0",
|
|
36
41
|
"typescript": "^5.9.3"
|
|
37
42
|
},
|
|
38
43
|
"scripts": {
|
|
39
|
-
"build": "
|
|
44
|
+
"build": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\" && tsup",
|
|
40
45
|
"dev": "tsup --watch"
|
|
41
46
|
}
|
|
42
47
|
}
|
package/src/channelTypeUtils.ts
CHANGED
|
@@ -22,7 +22,7 @@ export function isDirectChannel(channel: Channel | null | undefined): boolean {
|
|
|
22
22
|
|
|
23
23
|
/** Channel is a topic (sub-channel of a group channel) */
|
|
24
24
|
export function isTopicChannel(channel: Channel | null | undefined): boolean {
|
|
25
|
-
return channel ?
|
|
25
|
+
return channel ? channel.type === 'topic' || Boolean(channel.data?.parent_cid) : false;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/** Channel is a public group that users can join without invite */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { MediaLightbox } from './MediaLightbox';
|
|
3
|
+
import { getAvatarGradient } from '../utils/avatarColors';
|
|
3
4
|
import type { AvatarProps } from '../types';
|
|
4
5
|
|
|
5
6
|
export type { AvatarProps } from '../types';
|
|
@@ -85,7 +86,7 @@ export const Avatar: React.FC<AvatarProps> = React.memo(({
|
|
|
85
86
|
{/* 1. Underlying Fallback (Placeholder) */}
|
|
86
87
|
<div
|
|
87
88
|
className="ermis-avatar ermis-avatar--fallback"
|
|
88
|
-
style={contentStyle}
|
|
89
|
+
style={{ ...contentStyle, background: getAvatarGradient(name) }}
|
|
89
90
|
title={name}
|
|
90
91
|
>
|
|
91
92
|
{initials}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { useChatClient } from '../hooks/useChatClient';
|
|
3
|
+
import { useChatComponents } from '../context/ChatComponentsContext';
|
|
3
4
|
import { useBannedState } from '../hooks/useBannedState';
|
|
4
5
|
import { useBlockedState } from '../hooks/useBlockedState';
|
|
5
6
|
import { ForwardMessageModal } from './ForwardMessageModal';
|
|
@@ -25,18 +26,18 @@ export const Channel: React.FC<ChannelProps> = React.memo(({
|
|
|
25
26
|
className,
|
|
26
27
|
EmptyStateIndicator = DefaultEmpty,
|
|
27
28
|
HeaderComponent,
|
|
28
|
-
ForwardMessageModalComponent
|
|
29
|
+
ForwardMessageModalComponent: ForwardMessageModalProp,
|
|
29
30
|
}) => {
|
|
30
31
|
const { activeChannel, client, forwardingMessage, setForwardingMessage } = useChatClient();
|
|
32
|
+
const { ForwardMessageModalComponent: ForwardMessageModalContext } = useChatComponents();
|
|
33
|
+
|
|
34
|
+
const ForwardMessageModalView = ForwardMessageModalProp || ForwardMessageModalContext || ForwardMessageModal;
|
|
31
35
|
const { isBanned } = useBannedState(activeChannel, client.userID);
|
|
32
36
|
const { isBlocked } = useBlockedState(activeChannel, client.userID);
|
|
33
37
|
|
|
34
38
|
// Force re-render when channel info is updated via WS
|
|
35
39
|
const [channelUpdateCount, setChannelUpdateCount] = useState(0);
|
|
36
40
|
useEffect(() => {
|
|
37
|
-
|
|
38
|
-
console.log('---activeChannel--', activeChannel)
|
|
39
|
-
|
|
40
41
|
if (!activeChannel) return;
|
|
41
42
|
const sub = activeChannel.on('channel.updated', () => setChannelUpdateCount((c) => c + 1));
|
|
42
43
|
return () => sub.unsubscribe();
|
|
@@ -64,7 +65,7 @@ export const Channel: React.FC<ChannelProps> = React.memo(({
|
|
|
64
65
|
{HeaderComponent && headerData && <HeaderComponent {...headerData} />}
|
|
65
66
|
{children}
|
|
66
67
|
{forwardingMessage && (
|
|
67
|
-
<
|
|
68
|
+
<ForwardMessageModalView
|
|
68
69
|
message={forwardingMessage}
|
|
69
70
|
onDismiss={() => setForwardingMessage(null)}
|
|
70
71
|
/>
|
|
@@ -32,6 +32,8 @@ export function computeDefaultActions(
|
|
|
32
32
|
onAddTopic?: (channel: Channel) => void;
|
|
33
33
|
onEditTopic?: (channel: Channel) => void;
|
|
34
34
|
onToggleCloseTopic?: (channel: Channel, isClosed: boolean) => void;
|
|
35
|
+
onDeleteTopic?: (channel: Channel) => void;
|
|
36
|
+
onTruncateChannel?: (channel: Channel) => void;
|
|
35
37
|
isBlocked?: boolean;
|
|
36
38
|
actionLabels?: ChannelActionLabels;
|
|
37
39
|
actionIcons?: ChannelActionIcons;
|
|
@@ -99,6 +101,25 @@ export function computeDefaultActions(
|
|
|
99
101
|
}
|
|
100
102
|
},
|
|
101
103
|
});
|
|
104
|
+
|
|
105
|
+
// Direct channel: Truncate / Clear history
|
|
106
|
+
actions.push({
|
|
107
|
+
id: 'truncate',
|
|
108
|
+
label: actionLabels?.truncateChannel || 'Clear history',
|
|
109
|
+
icon: actionIcons?.TruncateChannelIcon || <TrashIcon />,
|
|
110
|
+
isDanger: true,
|
|
111
|
+
onClick: async (ch) => {
|
|
112
|
+
if (options?.onTruncateChannel) {
|
|
113
|
+
await options.onTruncateChannel(ch);
|
|
114
|
+
} else {
|
|
115
|
+
try {
|
|
116
|
+
await ch.truncate();
|
|
117
|
+
} catch (e) {
|
|
118
|
+
console.error('Error clearing channel history', e);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
102
123
|
} else if (isTopic) {
|
|
103
124
|
// Topic: Edit topic (owner & moder only)
|
|
104
125
|
if (canManageChannel(role)) {
|
|
@@ -118,8 +139,46 @@ export function computeDefaultActions(
|
|
|
118
139
|
label: isClosed ? (actionLabels?.reopenTopic || 'Reopen topic') : (actionLabels?.closeTopic || 'Close topic'),
|
|
119
140
|
icon: isClosed ? (actionIcons?.ReopenTopicIcon || <UnlockIcon />) : (actionIcons?.CloseTopicIcon || <LockIcon />),
|
|
120
141
|
isDanger: !isClosed,
|
|
121
|
-
onClick: (ch) => {
|
|
122
|
-
options?.onToggleCloseTopic
|
|
142
|
+
onClick: async (ch) => {
|
|
143
|
+
if (options?.onToggleCloseTopic) {
|
|
144
|
+
await options.onToggleCloseTopic(ch, isClosed);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Default behavior: call SDK API directly
|
|
148
|
+
const parentCid = ch.data?.parent_cid as string | undefined;
|
|
149
|
+
if (!parentCid) return;
|
|
150
|
+
try {
|
|
151
|
+
const client = ch.getClient();
|
|
152
|
+
const parentChannel = client.activeChannels[parentCid];
|
|
153
|
+
if (!parentChannel) return;
|
|
154
|
+
if (isClosed) {
|
|
155
|
+
await parentChannel.reopenTopic(ch.cid);
|
|
156
|
+
} else {
|
|
157
|
+
await parentChannel.closeTopic(ch.cid);
|
|
158
|
+
}
|
|
159
|
+
} catch (err) {
|
|
160
|
+
console.error('Failed to toggle topic close state', err);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Topic: Delete (owner only)
|
|
166
|
+
if (role === CHANNEL_ROLES.OWNER) {
|
|
167
|
+
actions.push({
|
|
168
|
+
id: 'delete_topic',
|
|
169
|
+
label: actionLabels?.deleteTopic || 'Delete topic',
|
|
170
|
+
icon: actionIcons?.DeleteTopicIcon || <TrashIcon />,
|
|
171
|
+
isDanger: true,
|
|
172
|
+
onClick: async (ch) => {
|
|
173
|
+
if (options?.onDeleteTopic) {
|
|
174
|
+
await options.onDeleteTopic(ch);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
await ch.delete();
|
|
179
|
+
} catch (err) {
|
|
180
|
+
console.error('Failed to delete topic', err);
|
|
181
|
+
}
|
|
123
182
|
},
|
|
124
183
|
});
|
|
125
184
|
}
|
|
@@ -157,9 +216,14 @@ export function computeDefaultActions(
|
|
|
157
216
|
isDanger: true,
|
|
158
217
|
onClick: async (ch) => {
|
|
159
218
|
try {
|
|
160
|
-
|
|
219
|
+
if (ch.data?.mls_enabled) {
|
|
220
|
+
await ch.leaveChannelE2ee(currentUserId);
|
|
221
|
+
} else {
|
|
222
|
+
await ch.removeMembers([currentUserId]);
|
|
223
|
+
}
|
|
161
224
|
} catch (e) {
|
|
162
225
|
console.error('Error leaving channel', e);
|
|
226
|
+
throw e;
|
|
163
227
|
}
|
|
164
228
|
},
|
|
165
229
|
});
|
|
@@ -47,9 +47,9 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
47
47
|
const { isPending } = usePendingState(activeChannel, client.userID);
|
|
48
48
|
const callContext = useContext(ErmisCallContext);
|
|
49
49
|
|
|
50
|
-
const isSkipped = client.userID
|
|
51
|
-
? isSkippedMember(activeChannel?.state?.members?.[client.userID]?.channel_role as string) ||
|
|
52
|
-
|
|
50
|
+
const isSkipped = client.userID
|
|
51
|
+
? isSkippedMember(activeChannel?.state?.members?.[client.userID]?.channel_role as string) ||
|
|
52
|
+
isSkippedMember(activeChannel?.state?.membership?.channel_role as string)
|
|
53
53
|
: false;
|
|
54
54
|
|
|
55
55
|
const actionDisabled = isPending || isSkipped;
|
|
@@ -59,9 +59,23 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
59
59
|
|
|
60
60
|
useEffect(() => {
|
|
61
61
|
if (!activeChannel) return;
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
const handleUpdate = () => setChannelUpdateCount((c) => c + 1);
|
|
63
|
+
|
|
64
|
+
const sub1 = activeChannel.on('channel.updated', handleUpdate);
|
|
65
|
+
|
|
66
|
+
// Also listen for client-level notifications that might affect this channel's roles/members
|
|
67
|
+
// We only care about this for messaging (direct) channels to update online status
|
|
68
|
+
const sub2 = client.on('notification.invite_accepted', (event) => {
|
|
69
|
+
if (event.cid === activeChannel.cid && isDirectChannel(activeChannel)) {
|
|
70
|
+
handleUpdate();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
sub1.unsubscribe();
|
|
76
|
+
sub2.unsubscribe();
|
|
77
|
+
};
|
|
78
|
+
}, [activeChannel, client]);
|
|
65
79
|
|
|
66
80
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
67
81
|
const channelName = useMemo(() =>
|
|
@@ -75,26 +89,7 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
75
89
|
[image, activeChannel?.data?.image, channelUpdateCount],
|
|
76
90
|
);
|
|
77
91
|
|
|
78
|
-
const teamName =
|
|
79
|
-
if (!activeChannel) return undefined;
|
|
80
|
-
|
|
81
|
-
// If it's a topic, derive from parent_cid
|
|
82
|
-
const parentCid = activeChannel.data?.parent_cid as string | undefined;
|
|
83
|
-
if (parentCid && client.activeChannels[parentCid]) {
|
|
84
|
-
return client.activeChannels[parentCid].data?.name || client.activeChannels[parentCid].cid;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// If it's a topics-enabled team channel (the general proxy), the proxy overrides data.name.
|
|
88
|
-
// We can pull the original name from the SDK cache.
|
|
89
|
-
if (hasTopicsEnabled(activeChannel)) {
|
|
90
|
-
const rawChannel = client.activeChannels[activeChannel.cid];
|
|
91
|
-
if (rawChannel && rawChannel.data?.name && rawChannel.data.name !== activeChannel.data?.name) {
|
|
92
|
-
return rawChannel.data.name;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return undefined;
|
|
97
|
-
}, [activeChannel, client.activeChannels]);
|
|
92
|
+
const teamName = undefined;
|
|
98
93
|
|
|
99
94
|
// ── Online Status (direct friend channels only) ──
|
|
100
95
|
const currentUserId = client.userID;
|
|
@@ -108,13 +103,13 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
108
103
|
if (memberId !== currentUserId) return memberId;
|
|
109
104
|
}
|
|
110
105
|
return undefined;
|
|
111
|
-
}, [activeChannel, currentUserId]);
|
|
106
|
+
}, [activeChannel, currentUserId, channelUpdateCount]);
|
|
112
107
|
|
|
113
108
|
// Check if this is a friend channel (both members are owner).
|
|
114
109
|
const isFriend = useMemo(() => {
|
|
115
110
|
if (!otherUserId || !currentUserId || !activeChannel) return false;
|
|
116
111
|
return isFriendChannel(activeChannel, otherUserId, currentUserId);
|
|
117
|
-
}, [activeChannel, otherUserId, currentUserId]);
|
|
112
|
+
}, [activeChannel, otherUserId, currentUserId, channelUpdateCount]);
|
|
118
113
|
|
|
119
114
|
// Derive online status from watchers + subscribe to realtime events.
|
|
120
115
|
const [onlineStatus, setOnlineStatus] = useState<OnlineStatus>('unknown');
|
|
@@ -158,12 +153,12 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
158
153
|
<div className={`ermis-channel-header${className ? ` ${className}` : ''}`}>
|
|
159
154
|
{activeChannel.data?.parent_cid ? (
|
|
160
155
|
<div className="ermis-channel-header__topic-avatar">
|
|
161
|
-
{channelImage && typeof channelImage === 'string' && channelImage.startsWith('emoji://')
|
|
162
|
-
? channelImage.replace('emoji://', '')
|
|
156
|
+
{channelImage && typeof channelImage === 'string' && channelImage.startsWith('emoji://')
|
|
157
|
+
? channelImage.replace('emoji://', '')
|
|
163
158
|
: '#'}
|
|
164
159
|
</div>
|
|
165
160
|
) : (
|
|
166
|
-
<AvatarComponent image={channelImage} name={teamName || channelName} size={
|
|
161
|
+
<AvatarComponent image={channelImage} name={teamName || channelName} size={44} />
|
|
167
162
|
)}
|
|
168
163
|
|
|
169
164
|
<div className="ermis-channel-header__info">
|
|
@@ -171,11 +166,6 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
171
166
|
renderTitle(activeChannel)
|
|
172
167
|
) : (
|
|
173
168
|
<div className="ermis-channel-header__title-container">
|
|
174
|
-
{teamName && (
|
|
175
|
-
<div className="ermis-channel-header__team-name">
|
|
176
|
-
{teamName}
|
|
177
|
-
</div>
|
|
178
|
-
)}
|
|
179
169
|
<div className="ermis-channel-header__name">{channelName}</div>
|
|
180
170
|
</div>
|
|
181
171
|
)}
|
|
@@ -216,7 +206,7 @@ export const ChannelHeader: React.FC<ChannelHeaderProps> = React.memo(({
|
|
|
216
206
|
</svg>
|
|
217
207
|
</button>
|
|
218
208
|
)}
|
|
219
|
-
|
|
209
|
+
|
|
220
210
|
{renderVideoCallButton ? (
|
|
221
211
|
renderVideoCallButton(() => callContext.createCall('video', activeChannel.cid || ''), actionDisabled)
|
|
222
212
|
) : (
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React, { useState, useMemo, useCallback } from 'react';
|
|
2
|
-
import { Modal } from '../Modal';
|
|
2
|
+
import { Modal as DefaultModal } from '../Modal';
|
|
3
3
|
import { UserPicker } from '../UserPicker';
|
|
4
4
|
import { Avatar } from '../Avatar';
|
|
5
|
+
import { useChatComponents } from '../../context/ChatComponentsContext';
|
|
5
6
|
import type { AddMemberModalProps, UserPickerUser } from '../../types';
|
|
6
7
|
|
|
7
8
|
export const AddMemberModal: React.FC<AddMemberModalProps> = ({
|
|
@@ -19,6 +20,8 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({
|
|
|
19
20
|
UserItemComponent,
|
|
20
21
|
SearchInputComponent,
|
|
21
22
|
}) => {
|
|
23
|
+
const { ModalComponent } = useChatComponents();
|
|
24
|
+
const Modal = ModalComponent || DefaultModal;
|
|
22
25
|
const [selectedUsers, setSelectedUsers] = useState<UserPickerUser[]>([]);
|
|
23
26
|
const [isAdding, setIsAdding] = useState(false);
|
|
24
27
|
|
|
@@ -37,7 +40,13 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({
|
|
|
37
40
|
if (selectedUsers.length === 0 || isAdding) return;
|
|
38
41
|
try {
|
|
39
42
|
setIsAdding(true);
|
|
40
|
-
|
|
43
|
+
const memberIds = selectedUsers.map(u => u.id);
|
|
44
|
+
const encryptionManager = channel.getClient().encryptionManager;
|
|
45
|
+
if (channel.data?.mls_enabled && encryptionManager?.initialized && channel.id && channel.cid) {
|
|
46
|
+
await encryptionManager.addMembers(channel.type, channel.id, channel.cid, memberIds);
|
|
47
|
+
} else {
|
|
48
|
+
await channel.addMembers(memberIds);
|
|
49
|
+
}
|
|
41
50
|
onClose();
|
|
42
51
|
} catch (err) {
|
|
43
52
|
console.error('Failed to add members:', err);
|
|
@@ -62,6 +71,7 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({
|
|
|
62
71
|
<Modal isOpen onClose={onClose} title={title} maxWidth="480px" footer={footer}>
|
|
63
72
|
<UserPicker
|
|
64
73
|
mode="checkbox"
|
|
74
|
+
friendsOnly={true}
|
|
65
75
|
onSelectionChange={handleSelectionChange}
|
|
66
76
|
excludeUserIds={excludeUserIds}
|
|
67
77
|
pageSize={30}
|