@ermis-network/ermis-chat-react 1.0.9 → 2.0.0
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/index.cjs +15288 -4203
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +701 -195
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +862 -94
- package/dist/index.d.ts +862 -94
- package/dist/index.mjs +15238 -4179
- 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 -2
- package/src/components/ChannelActions.tsx +61 -2
- package/src/components/ChannelHeader.tsx +19 -5
- package/src/components/ChannelInfo/AddMemberModal.tsx +5 -1
- package/src/components/ChannelInfo/ChannelInfo.tsx +330 -187
- package/src/components/ChannelInfo/ChannelInfoTabs.tsx +59 -297
- package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +30 -174
- package/src/components/ChannelInfo/EditChannelModal.tsx +4 -1
- package/src/components/ChannelInfo/MediaGridItem.tsx +12 -2
- 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 +386 -0
- package/src/components/ChannelInfo/useChannelSettings.ts +212 -0
- package/src/components/ChannelInfo/useMessageSearch.tsx +141 -0
- package/src/components/ChannelList.tsx +177 -290
- package/src/components/CreateChannelModal.tsx +166 -88
- 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/FlatTopicGroupItem.tsx +232 -0
- package/src/components/ForwardMessageModal.tsx +31 -77
- package/src/components/MediaLightbox.tsx +62 -40
- package/src/components/MentionSuggestions.tsx +47 -35
- package/src/components/MessageActionsBox.tsx +4 -1
- package/src/components/MessageInput.tsx +126 -7
- package/src/components/MessageInputDefaults.tsx +127 -1
- package/src/components/MessageItem.tsx +93 -26
- package/src/components/MessageQuickReactions.tsx +153 -26
- package/src/components/MessageReactions.tsx +2 -1
- package/src/components/MessageRenderers.tsx +111 -39
- package/src/components/Panel.tsx +1 -14
- package/src/components/PinnedMessages.tsx +17 -5
- package/src/components/PreviewOverlay.tsx +24 -0
- package/src/components/ReadReceipts.tsx +2 -1
- package/src/components/TopicList.tsx +221 -0
- package/src/components/TopicModal.tsx +4 -1
- package/src/components/TypingIndicator.tsx +14 -5
- package/src/components/UserPicker.tsx +87 -10
- package/src/components/VirtualMessageList.tsx +106 -20
- package/src/context/ChatComponentsContext.tsx +14 -0
- package/src/context/ChatProvider.tsx +18 -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 +72 -20
- package/src/hooks/useChannelMessages.ts +72 -10
- package/src/hooks/useChannelRowUpdates.ts +24 -5
- package/src/hooks/useChatUser.ts +31 -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/useForwardMessage.ts +112 -0
- package/src/hooks/useInviteChannels.ts +88 -0
- package/src/hooks/useInviteCount.ts +104 -0
- package/src/hooks/useMentions.ts +0 -1
- package/src/hooks/useMessageActions.ts +13 -10
- package/src/hooks/usePendingState.ts +21 -4
- package/src/hooks/usePreviewState.ts +69 -0
- package/src/hooks/useStickerPicker.ts +62 -0
- package/src/hooks/useTopicGroupUpdates.ts +197 -0
- package/src/index.ts +56 -6
- package/src/messageTypeUtils.ts +13 -1
- package/src/styles/_base.css +0 -1
- package/src/styles/_call-ui.css +59 -2
- package/src/styles/_channel-info.css +41 -4
- package/src/styles/_channel-list.css +97 -57
- package/src/styles/_create-channel-modal.css +10 -0
- package/src/styles/_forward-modal.css +16 -1
- package/src/styles/_media-lightbox.css +32 -0
- package/src/styles/_mentions.css +1 -1
- package/src/styles/_message-actions.css +3 -4
- package/src/styles/_message-bubble.css +286 -107
- package/src/styles/_message-input.css +131 -0
- package/src/styles/_message-list.css +33 -17
- package/src/styles/_message-quick-reactions.css +40 -9
- package/src/styles/_message-reactions.css +4 -0
- package/src/styles/_modal.css +2 -1
- package/src/styles/_preview-overlay.css +38 -0
- package/src/styles/_tokens.css +17 -15
- package/src/styles/_typing-indicator.css +7 -1
- package/src/styles/index.css +1 -0
- package/src/types.ts +362 -14
- package/src/utils/avatarColors.ts +48 -0
- package/src/utils.ts +193 -10
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import React, { useState,
|
|
1
|
+
import React, { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
+
import { Virtualizer as _Virtualizer } from 'virtua';
|
|
3
|
+
const Virtualizer = _Virtualizer as any;
|
|
2
4
|
import { useChatClient } from '../../hooks/useChatClient';
|
|
3
5
|
import { useBannedState } from '../../hooks/useBannedState';
|
|
4
6
|
import { useBlockedState } from '../../hooks/useBlockedState';
|
|
7
|
+
import { usePreviewState } from '../../hooks/usePreviewState';
|
|
5
8
|
import { Avatar } from '../Avatar';
|
|
6
|
-
import {
|
|
9
|
+
import { DefaultChannelInfoTabHeader } from './ChannelInfoTabs';
|
|
10
|
+
import { useChannelInfoTabs } from './useChannelInfoTabs';
|
|
7
11
|
import { AddMemberModal } from './AddMemberModal';
|
|
8
12
|
import { EditChannelModal } from './EditChannelModal';
|
|
9
13
|
import { TopicModal } from '../TopicModal';
|
|
10
14
|
import { MessageSearchPanel } from './MessageSearchPanel';
|
|
11
15
|
import { ChannelSettingsPanel } from './ChannelSettingsPanel';
|
|
16
|
+
import { MediaLightbox } from '../MediaLightbox';
|
|
17
|
+
import { PENDING_STYLE, READY_STYLE } from './utils';
|
|
12
18
|
import type {
|
|
13
19
|
ChannelInfoProps,
|
|
14
20
|
ChannelInfoHeaderProps,
|
|
@@ -19,6 +25,22 @@ import { useChannelMembers, useChannelProfile } from '../../hooks/useChannelData
|
|
|
19
25
|
import { isGroupChannel, isTopicChannel } from '../../channelTypeUtils';
|
|
20
26
|
import { canManageChannel, CHANNEL_ROLES } from '../../channelRoleUtils';
|
|
21
27
|
|
|
28
|
+
const MemoizedVirtualizer = React.memo(({ scrollRef, startMargin, data, renderItem, overscan = 10 }: any) => (
|
|
29
|
+
<Virtualizer scrollRef={scrollRef} startMargin={startMargin} data={data} overscan={overscan}>
|
|
30
|
+
{renderItem}
|
|
31
|
+
</Virtualizer>
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
const PinIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 4.5l-4 4l-4 1.5l-1.5 1.5l7 7l1.5 -1.5l1.5 -4l4 -4" /><path d="M9 15l-4.5 4.5" /><path d="M14.5 4l5.5 5.5" /></svg>);
|
|
35
|
+
const UnpinIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 4.5l-4 4l-4 1.5l-1.5 1.5l7 7l1.5 -1.5l1.5 -4l4 -4" /><path d="M9 15l-4.5 4.5" /><path d="M14.5 4l5.5 5.5" /><line x1="3" y1="3" x2="21" y2="21" /></svg>);
|
|
36
|
+
const SearchIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>);
|
|
37
|
+
const SettingsIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>);
|
|
38
|
+
const DeleteIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6V20a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>);
|
|
39
|
+
const LeaveIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>);
|
|
40
|
+
const CloseTopicIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>);
|
|
41
|
+
const ReopenTopicIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 9.9-1" /></svg>);
|
|
42
|
+
const BlockIcon = () => (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10" /><line x1="4.93" y1="4.93" x2="19.07" y2="19.07" /></svg>);
|
|
43
|
+
|
|
22
44
|
export const DefaultChannelInfoHeader: React.FC<ChannelInfoHeaderProps> = React.memo(({ title, onClose }) => {
|
|
23
45
|
return (
|
|
24
46
|
<div className="ermis-channel-info__header">
|
|
@@ -93,30 +115,31 @@ export const DefaultChannelInfoCover: React.FC<ChannelInfoCoverProps> = React.me
|
|
|
93
115
|
DefaultChannelInfoCover.displayName = 'DefaultChannelInfoCover';
|
|
94
116
|
|
|
95
117
|
export const DefaultChannelInfoActions: React.FC<ChannelInfoActionsProps> = React.memo(({
|
|
96
|
-
onSearchClick, onSettingsClick, onLeaveChannel, onDeleteChannel,
|
|
97
|
-
onBlockUser, onUnblockUser, onCloseTopic, onReopenTopic,
|
|
98
|
-
isTeamChannel, isTopic, isClosedTopic, isBlocked, currentUserRole,
|
|
99
|
-
searchLabel = 'Search', settingsLabel = 'Settings', deleteLabel = 'Delete', leaveLabel = 'Leave',
|
|
100
|
-
blockLabel = 'Block', unblockLabel = 'Unblock',
|
|
118
|
+
onSearchClick, onSettingsClick, onLeaveChannel, onDeleteChannel, onDeleteTopic, onTruncateChannel,
|
|
119
|
+
onBlockUser, onUnblockUser, onPin, onUnpin, onCloseTopic, onReopenTopic,
|
|
120
|
+
isTeamChannel, isTopic, isClosedTopic, isBlocked, isPinned, currentUserRole,
|
|
121
|
+
searchLabel = 'Search', settingsLabel = 'Settings', deleteLabel = 'Delete', truncateLabel = 'Clear history', leaveLabel = 'Leave',
|
|
122
|
+
blockLabel = 'Block', unblockLabel = 'Unblock', pinLabel = 'Pin', unpinLabel = 'Unpin',
|
|
123
|
+
closeTopicLabel = 'Close Topic', reopenTopicLabel = 'Reopen Topic', deleteTopicLabel = 'Delete Topic'
|
|
101
124
|
}) => {
|
|
102
125
|
return (
|
|
103
126
|
<div className="ermis-channel-info__actions">
|
|
104
127
|
<button className="ermis-channel-info__action-btn" onClick={onSearchClick} disabled={isBlocked}>
|
|
105
128
|
<div className="ermis-channel-info__action-icon">
|
|
106
|
-
<
|
|
107
|
-
<circle cx="11" cy="11" r="8"></circle>
|
|
108
|
-
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
109
|
-
</svg>
|
|
129
|
+
<SearchIcon />
|
|
110
130
|
</div>
|
|
111
131
|
<span>{searchLabel}</span>
|
|
112
132
|
</button>
|
|
133
|
+
<button className="ermis-channel-info__action-btn" onClick={isPinned ? onUnpin : onPin} disabled={isBlocked}>
|
|
134
|
+
<div className="ermis-channel-info__action-icon">
|
|
135
|
+
{isPinned ? <UnpinIcon /> : <PinIcon />}
|
|
136
|
+
</div>
|
|
137
|
+
<span>{isPinned ? unpinLabel : pinLabel}</span>
|
|
138
|
+
</button>
|
|
113
139
|
{isTeamChannel && canManageChannel(currentUserRole) && (
|
|
114
140
|
<button className="ermis-channel-info__action-btn" onClick={onSettingsClick}>
|
|
115
141
|
<div className="ermis-channel-info__action-icon">
|
|
116
|
-
<
|
|
117
|
-
<circle cx="12" cy="12" r="3"></circle>
|
|
118
|
-
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
|
119
|
-
</svg>
|
|
142
|
+
<SettingsIcon />
|
|
120
143
|
</div>
|
|
121
144
|
<span>{settingsLabel}</span>
|
|
122
145
|
</button>
|
|
@@ -125,21 +148,14 @@ export const DefaultChannelInfoActions: React.FC<ChannelInfoActionsProps> = Reac
|
|
|
125
148
|
currentUserRole === CHANNEL_ROLES.OWNER ? (
|
|
126
149
|
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onDeleteChannel}>
|
|
127
150
|
<div className="ermis-channel-info__action-icon">
|
|
128
|
-
<
|
|
129
|
-
<polyline points="3 6 5 6 21 6"></polyline>
|
|
130
|
-
<path d="M19 6V20a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
131
|
-
</svg>
|
|
151
|
+
<DeleteIcon />
|
|
132
152
|
</div>
|
|
133
153
|
<span>{deleteLabel}</span>
|
|
134
154
|
</button>
|
|
135
155
|
) : (
|
|
136
156
|
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onLeaveChannel}>
|
|
137
157
|
<div className="ermis-channel-info__action-icon">
|
|
138
|
-
<
|
|
139
|
-
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
|
140
|
-
<polyline points="16 17 21 12 16 7"></polyline>
|
|
141
|
-
<line x1="21" y1="12" x2="9" y2="12"></line>
|
|
142
|
-
</svg>
|
|
158
|
+
<LeaveIcon />
|
|
143
159
|
</div>
|
|
144
160
|
<span>{leaveLabel}</span>
|
|
145
161
|
</button>
|
|
@@ -150,48 +166,55 @@ export const DefaultChannelInfoActions: React.FC<ChannelInfoActionsProps> = Reac
|
|
|
150
166
|
isClosedTopic ? (
|
|
151
167
|
<button className="ermis-channel-info__action-btn" onClick={onReopenTopic}>
|
|
152
168
|
<div className="ermis-channel-info__action-icon">
|
|
153
|
-
<
|
|
154
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
|
|
155
|
-
<path d="M7 11V7a5 5 0 0 1 9.9-1" />
|
|
156
|
-
</svg>
|
|
169
|
+
<ReopenTopicIcon />
|
|
157
170
|
</div>
|
|
158
171
|
<span>{reopenTopicLabel}</span>
|
|
159
172
|
</button>
|
|
160
173
|
) : (
|
|
161
174
|
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onCloseTopic}>
|
|
162
175
|
<div className="ermis-channel-info__action-icon">
|
|
163
|
-
<
|
|
164
|
-
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
|
|
165
|
-
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
|
166
|
-
</svg>
|
|
176
|
+
<CloseTopicIcon />
|
|
167
177
|
</div>
|
|
168
178
|
<span>{closeTopicLabel}</span>
|
|
169
179
|
</button>
|
|
170
180
|
)
|
|
171
181
|
)}
|
|
172
|
-
{/*
|
|
182
|
+
{/* Topics: Delete Topic for owner */}
|
|
183
|
+
{isTopic && currentUserRole === CHANNEL_ROLES.OWNER && onDeleteTopic && (
|
|
184
|
+
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onDeleteTopic}>
|
|
185
|
+
<div className="ermis-channel-info__action-icon">
|
|
186
|
+
<DeleteIcon />
|
|
187
|
+
</div>
|
|
188
|
+
<span>{deleteTopicLabel}</span>
|
|
189
|
+
</button>
|
|
190
|
+
)}
|
|
191
|
+
{/* Block/Unblock & Truncate — messaging (1-1) channels only */}
|
|
173
192
|
{!isTeamChannel && !isTopic && (
|
|
174
|
-
|
|
193
|
+
<>
|
|
194
|
+
{onTruncateChannel && (
|
|
195
|
+
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onTruncateChannel}>
|
|
196
|
+
<div className="ermis-channel-info__action-icon">
|
|
197
|
+
<DeleteIcon />
|
|
198
|
+
</div>
|
|
199
|
+
<span>{truncateLabel}</span>
|
|
200
|
+
</button>
|
|
201
|
+
)}
|
|
202
|
+
{isBlocked ? (
|
|
175
203
|
<button className="ermis-channel-info__action-btn" onClick={onUnblockUser}>
|
|
176
204
|
<div className="ermis-channel-info__action-icon">
|
|
177
|
-
<
|
|
178
|
-
<circle cx="12" cy="12" r="10" />
|
|
179
|
-
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
|
180
|
-
</svg>
|
|
205
|
+
<BlockIcon />
|
|
181
206
|
</div>
|
|
182
207
|
<span>{unblockLabel}</span>
|
|
183
208
|
</button>
|
|
184
209
|
) : (
|
|
185
210
|
<button className="ermis-channel-info__action-btn ermis-channel-info__action-btn--danger" onClick={onBlockUser}>
|
|
186
211
|
<div className="ermis-channel-info__action-icon">
|
|
187
|
-
<
|
|
188
|
-
<circle cx="12" cy="12" r="10" />
|
|
189
|
-
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
|
190
|
-
</svg>
|
|
212
|
+
<BlockIcon />
|
|
191
213
|
</div>
|
|
192
214
|
<span>{blockLabel}</span>
|
|
193
215
|
</button>
|
|
194
|
-
|
|
216
|
+
)}
|
|
217
|
+
</>
|
|
195
218
|
)}
|
|
196
219
|
</div>
|
|
197
220
|
);
|
|
@@ -205,16 +228,20 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
205
228
|
AvatarComponent = Avatar,
|
|
206
229
|
onClose,
|
|
207
230
|
title: titleProp,
|
|
231
|
+
isVisible = true,
|
|
208
232
|
HeaderComponent = DefaultChannelInfoHeader,
|
|
209
233
|
CoverComponent = DefaultChannelInfoCover,
|
|
210
234
|
ActionsComponent = DefaultChannelInfoActions,
|
|
211
|
-
|
|
235
|
+
TabHeaderComponent = DefaultChannelInfoTabHeader,
|
|
212
236
|
AddMemberModalComponent,
|
|
213
237
|
EditChannelModalComponent,
|
|
238
|
+
EditTopicModalComponent,
|
|
214
239
|
actionsSearchLabel,
|
|
215
240
|
actionsSettingsLabel,
|
|
216
241
|
actionsDeleteLabel,
|
|
242
|
+
actionsTruncateLabel,
|
|
217
243
|
actionsLeaveLabel,
|
|
244
|
+
actionsCreateTopicLabel,
|
|
218
245
|
MemberItemComponent,
|
|
219
246
|
MediaItemComponent,
|
|
220
247
|
LinkItemComponent,
|
|
@@ -224,12 +251,17 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
224
251
|
onSearchClick,
|
|
225
252
|
onLeaveChannel: onLeaveChannelProp,
|
|
226
253
|
onDeleteChannel: onDeleteChannelProp,
|
|
254
|
+
onTruncateChannel: onTruncateChannelProp,
|
|
255
|
+
onDeleteTopic: onDeleteTopicProp,
|
|
256
|
+
onCreateTopic: onCreateTopicProp,
|
|
227
257
|
onAddMemberClick,
|
|
228
258
|
onRemoveMember: onRemoveMemberProp,
|
|
229
259
|
onBanMember: onBanMemberProp,
|
|
230
260
|
onUnbanMember: onUnbanMemberProp,
|
|
231
261
|
onPromoteMember: onPromoteMemberProp,
|
|
232
262
|
onDemoteMember: onDemoteMemberProp,
|
|
263
|
+
onPinChannel: onPinChannelProp,
|
|
264
|
+
onUnpinChannel: onUnpinChannelProp,
|
|
233
265
|
// Add Member customization
|
|
234
266
|
addMemberModalTitle,
|
|
235
267
|
addMemberSearchPlaceholder,
|
|
@@ -239,6 +271,8 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
239
271
|
addMemberAddingLabel,
|
|
240
272
|
addMemberAddedLabel,
|
|
241
273
|
addMemberButtonLabel,
|
|
274
|
+
MessageSearchPanelComponent,
|
|
275
|
+
ChannelSettingsPanelComponent,
|
|
242
276
|
AddMemberButtonComponent,
|
|
243
277
|
// Edit Channel customization
|
|
244
278
|
onEditChannel: onEditChannelProp,
|
|
@@ -260,18 +294,27 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
260
294
|
onUnblockUser: onUnblockUserProp,
|
|
261
295
|
actionsBlockLabel,
|
|
262
296
|
actionsUnblockLabel,
|
|
297
|
+
actionsPinLabel,
|
|
298
|
+
actionsUnpinLabel,
|
|
299
|
+
actionsPinTopicLabel,
|
|
300
|
+
actionsUnpinTopicLabel,
|
|
263
301
|
actionsCloseTopicLabel,
|
|
264
302
|
actionsReopenTopicLabel,
|
|
303
|
+
actionsDeleteTopicLabel,
|
|
265
304
|
// Settings panel customizations
|
|
266
305
|
settingsWorkspaceTopicsTitle,
|
|
267
306
|
settingsTopicsFeatureName,
|
|
268
307
|
settingsTopicsFeatureDescription,
|
|
308
|
+
roleLabels,
|
|
269
309
|
} = props;
|
|
270
310
|
|
|
271
311
|
const { activeChannel, client } = useChatClient();
|
|
312
|
+
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
|
|
313
|
+
|
|
272
314
|
const channel = channelProp || activeChannel;
|
|
273
315
|
const { isBanned } = useBannedState(channel, client?.userID);
|
|
274
316
|
const { isBlocked } = useBlockedState(channel, client?.userID);
|
|
317
|
+
const { isPreviewMode } = usePreviewState(channel, client?.userID);
|
|
275
318
|
|
|
276
319
|
const currentUserId = client?.userID;
|
|
277
320
|
const currentUserRole = currentUserId ? channel?.state?.members?.[currentUserId]?.channel_role : undefined;
|
|
@@ -350,6 +393,18 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
350
393
|
try { await channel.unblockUser(); } catch (e) { console.error('Error unblocking user', e); }
|
|
351
394
|
}, [channel, onUnblockUserProp]);
|
|
352
395
|
|
|
396
|
+
const handlePinChannel = useCallback(async () => {
|
|
397
|
+
if (onPinChannelProp) return onPinChannelProp();
|
|
398
|
+
if (!channel) return;
|
|
399
|
+
try { await channel.pin(); } catch (e) { console.error('Error pinning channel', e); }
|
|
400
|
+
}, [channel, onPinChannelProp]);
|
|
401
|
+
|
|
402
|
+
const handleUnpinChannel = useCallback(async () => {
|
|
403
|
+
if (onUnpinChannelProp) return onUnpinChannelProp();
|
|
404
|
+
if (!channel) return;
|
|
405
|
+
try { await channel.unpin(); } catch (e) { console.error('Error unpanning channel', e); }
|
|
406
|
+
}, [channel, onUnpinChannelProp]);
|
|
407
|
+
|
|
353
408
|
const handleCloseTopic = useCallback(async () => {
|
|
354
409
|
if (!channel || !parentChannel) return;
|
|
355
410
|
try { await parentChannel.closeTopic(channel.cid); } catch (e) { console.error('Error closing topic', e); }
|
|
@@ -360,8 +415,14 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
360
415
|
try { await parentChannel.reopenTopic(channel.cid); } catch (e) { console.error('Error reopening topic', e); }
|
|
361
416
|
}, [channel, parentChannel]);
|
|
362
417
|
|
|
418
|
+
const handleDeleteTopic = useCallback(async () => {
|
|
419
|
+
if (onDeleteTopicProp) return onDeleteTopicProp(channel as any);
|
|
420
|
+
if (!channel) return;
|
|
421
|
+
try { await channel.delete(); } catch (e) { console.error('Error deleting topic', e); }
|
|
422
|
+
}, [channel, onDeleteTopicProp]);
|
|
423
|
+
|
|
363
424
|
const { members } = useChannelMembers(channel);
|
|
364
|
-
const { channelName: profileChannelName, channelImage, channelDescription } = useChannelProfile(channel);
|
|
425
|
+
const { channelName: profileChannelName, channelImage, channelDescription, isPinned } = useChannelProfile(channel);
|
|
365
426
|
|
|
366
427
|
let finalChannelName = profileChannelName;
|
|
367
428
|
let finalParentChannelName = parentChannelName;
|
|
@@ -397,161 +458,243 @@ export const ChannelInfo: React.FC<ChannelInfoProps> = React.memo((props) => {
|
|
|
397
458
|
setShowAddMemberModal(true);
|
|
398
459
|
}, [onAddMemberClick]);
|
|
399
460
|
|
|
400
|
-
|
|
461
|
+
// virtualizer anchors for page-level virtualization
|
|
462
|
+
const virtualizerAnchorRef = useRef<HTMLDivElement | null>(null);
|
|
463
|
+
const [startMargin, setStartMargin] = useState(0);
|
|
464
|
+
|
|
465
|
+
// Use the headless tabs hook
|
|
466
|
+
const tabs = useChannelInfoTabs({
|
|
467
|
+
channel: channel as any,
|
|
468
|
+
members: members as any,
|
|
469
|
+
AvatarComponent,
|
|
470
|
+
currentUserId,
|
|
471
|
+
currentUserRole,
|
|
472
|
+
onAddMemberClick: isTeamChannel && !isPreviewMode ? handleAddMemberClick : undefined,
|
|
473
|
+
onRemoveMember: handleRemoveMember,
|
|
474
|
+
onBanMember: handleBanMember,
|
|
475
|
+
onUnbanMember: handleUnbanMember,
|
|
476
|
+
onPromoteMember: handlePromoteMember,
|
|
477
|
+
onDemoteMember: handleDemoteMember,
|
|
478
|
+
addMemberButtonLabel,
|
|
479
|
+
AddMemberButtonComponent,
|
|
480
|
+
MemberItemComponent,
|
|
481
|
+
MediaItemComponent,
|
|
482
|
+
LinkItemComponent,
|
|
483
|
+
FileItemComponent,
|
|
484
|
+
EmptyStateComponent,
|
|
485
|
+
LoadingComponent,
|
|
486
|
+
isVisible,
|
|
487
|
+
isPreviewMode,
|
|
488
|
+
roleLabels,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Calculate startMargin for Virtualizer (distance from body top to virtualizer anchor)
|
|
492
|
+
useEffect(() => {
|
|
493
|
+
const body = scrollContainerRef.current;
|
|
494
|
+
const anchor = virtualizerAnchorRef.current;
|
|
495
|
+
if (!body || !anchor) return;
|
|
496
|
+
const bodyRect = body.getBoundingClientRect();
|
|
497
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
498
|
+
setStartMargin(anchorRect.top - bodyRect.top + body.scrollTop);
|
|
499
|
+
}, [isVisible, tabs.activeTab, isBanned, isPreviewMode]);
|
|
401
500
|
|
|
402
501
|
if (!channel) return null;
|
|
403
502
|
|
|
404
503
|
return (
|
|
405
504
|
<div className={`ermis-channel-info ${className}`.trim()}>
|
|
406
505
|
<HeaderComponent title={title} onClose={onClose} />
|
|
506
|
+
<div className="ermis-channel-info__body" ref={scrollContainerRef}>
|
|
507
|
+
<CoverComponent
|
|
508
|
+
channelName={finalChannelName}
|
|
509
|
+
channelImage={channelImage}
|
|
510
|
+
channelDescription={channelDescription}
|
|
511
|
+
AvatarComponent={AvatarComponent}
|
|
512
|
+
canEdit={canEditChannel}
|
|
513
|
+
onEditClick={handleEditChannelClick}
|
|
514
|
+
isPublic={Boolean(channel?.data?.public)}
|
|
515
|
+
isTeamChannel={isTeamChannel}
|
|
516
|
+
parentChannelName={finalParentChannelName}
|
|
517
|
+
isTopic={isTopic}
|
|
518
|
+
/>
|
|
407
519
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
onBanMember={handleBanMember}
|
|
465
|
-
onUnbanMember={handleUnbanMember}
|
|
466
|
-
onPromoteMember={handlePromoteMember}
|
|
467
|
-
onDemoteMember={handleDemoteMember}
|
|
468
|
-
addMemberButtonLabel={addMemberButtonLabel}
|
|
469
|
-
AddMemberButtonComponent={AddMemberButtonComponent}
|
|
470
|
-
MemberItemComponent={MemberItemComponent}
|
|
471
|
-
MediaItemComponent={MediaItemComponent}
|
|
472
|
-
LinkItemComponent={LinkItemComponent}
|
|
473
|
-
FileItemComponent={FileItemComponent}
|
|
474
|
-
EmptyStateComponent={EmptyStateComponent}
|
|
475
|
-
LoadingComponent={LoadingComponent}
|
|
476
|
-
/>
|
|
477
|
-
|
|
478
|
-
{showAddMemberModal && (() => {
|
|
479
|
-
const ModalComp = AddMemberModalComponent || AddMemberModal;
|
|
480
|
-
return (
|
|
481
|
-
<ModalComp
|
|
482
|
-
channel={channel}
|
|
483
|
-
currentMembers={members as any}
|
|
484
|
-
onClose={() => setShowAddMemberModal(false)}
|
|
485
|
-
AvatarComponent={AvatarComponent}
|
|
486
|
-
title={addMemberModalTitle}
|
|
487
|
-
searchPlaceholder={addMemberSearchPlaceholder}
|
|
488
|
-
loadingText={addMemberLoadingText}
|
|
489
|
-
emptyText={addMemberEmptyText}
|
|
490
|
-
addLabel={addMemberAddLabel}
|
|
491
|
-
addingLabel={addMemberAddingLabel}
|
|
492
|
-
addedLabel={addMemberAddedLabel}
|
|
493
|
-
/>
|
|
494
|
-
);
|
|
495
|
-
})()}
|
|
496
|
-
|
|
497
|
-
{showEditChannelModal && (() => {
|
|
498
|
-
const EditComp = EditChannelModalComponent || EditChannelModal;
|
|
499
|
-
return (
|
|
500
|
-
<EditComp
|
|
501
|
-
channel={channel}
|
|
502
|
-
onClose={() => setShowEditChannelModal(false)}
|
|
503
|
-
onSave={onEditChannelProp}
|
|
504
|
-
AvatarComponent={AvatarComponent}
|
|
505
|
-
title={editChannelModalTitle}
|
|
506
|
-
nameLabel={editChannelNameLabel}
|
|
507
|
-
descriptionLabel={editChannelDescriptionLabel}
|
|
508
|
-
namePlaceholder={editChannelNamePlaceholder}
|
|
509
|
-
descriptionPlaceholder={editChannelDescriptionPlaceholder}
|
|
510
|
-
publicLabel={editChannelPublicLabel}
|
|
511
|
-
saveLabel={editChannelSaveLabel}
|
|
512
|
-
cancelLabel={editChannelCancelLabel}
|
|
513
|
-
savingLabel={editChannelSavingLabel}
|
|
514
|
-
changeAvatarLabel={editChannelChangeAvatarLabel}
|
|
515
|
-
imageAccept={editChannelImageAccept}
|
|
516
|
-
maxImageSize={editChannelMaxImageSize}
|
|
517
|
-
maxImageSizeError={editChannelMaxImageSizeError}
|
|
520
|
+
{isBanned && (
|
|
521
|
+
<div className="ermis-channel-info__banned-banner">
|
|
522
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
523
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
524
|
+
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line>
|
|
525
|
+
</svg>
|
|
526
|
+
<span className="ermis-channel-info__banned-banner-text">You have been banned from this channel</span>
|
|
527
|
+
</div>
|
|
528
|
+
)}
|
|
529
|
+
{!isBanned && isPreviewMode && (
|
|
530
|
+
<div className="ermis-channel-info__preview-actions">
|
|
531
|
+
<button
|
|
532
|
+
className="ermis-channel-info__join-btn"
|
|
533
|
+
onClick={() => channel?.acceptInvite('join').catch(e => console.error('Failed to join public channel', e))}
|
|
534
|
+
>
|
|
535
|
+
Join Channel
|
|
536
|
+
</button>
|
|
537
|
+
</div>
|
|
538
|
+
)}
|
|
539
|
+
{!isBanned && (
|
|
540
|
+
<>
|
|
541
|
+
{!isPreviewMode && (
|
|
542
|
+
<ActionsComponent
|
|
543
|
+
onSearchClick={() => setShowSearchPanel(true)}
|
|
544
|
+
onSettingsClick={() => setShowSettingsPanel(true)}
|
|
545
|
+
onLeaveChannel={handleLeaveChannel}
|
|
546
|
+
onDeleteChannel={handleDeleteChannel}
|
|
547
|
+
onTruncateChannel={onTruncateChannelProp ? () => onTruncateChannelProp(channel) : undefined}
|
|
548
|
+
onBlockUser={handleBlockUser}
|
|
549
|
+
onUnblockUser={handleUnblockUser}
|
|
550
|
+
onPin={handlePinChannel}
|
|
551
|
+
onUnpin={handleUnpinChannel}
|
|
552
|
+
onCloseTopic={handleCloseTopic}
|
|
553
|
+
onReopenTopic={handleReopenTopic}
|
|
554
|
+
onDeleteTopic={handleDeleteTopic}
|
|
555
|
+
onCreateTopic={onCreateTopicProp ? () => onCreateTopicProp(channel) : undefined}
|
|
556
|
+
isTeamChannel={isTeamChannel}
|
|
557
|
+
isTopic={isTopic}
|
|
558
|
+
isClosedTopic={isClosedTopic}
|
|
559
|
+
isBlocked={isBlocked}
|
|
560
|
+
isPinned={isPinned}
|
|
561
|
+
topicsEnabled={channel?.data?.topics_enabled === true}
|
|
562
|
+
currentUserRole={currentUserRole}
|
|
563
|
+
searchLabel={actionsSearchLabel}
|
|
564
|
+
settingsLabel={actionsSettingsLabel}
|
|
565
|
+
deleteLabel={actionsDeleteLabel}
|
|
566
|
+
truncateLabel={actionsTruncateLabel}
|
|
567
|
+
leaveLabel={actionsLeaveLabel}
|
|
568
|
+
blockLabel={actionsBlockLabel}
|
|
569
|
+
unblockLabel={actionsUnblockLabel}
|
|
570
|
+
pinLabel={isTopic ? (actionsPinTopicLabel || 'Pin topic') : (actionsPinLabel || 'Pin channel')}
|
|
571
|
+
unpinLabel={isTopic ? (actionsUnpinTopicLabel || 'Unpin topic') : (actionsUnpinLabel || 'Unpin channel')}
|
|
572
|
+
closeTopicLabel={actionsCloseTopicLabel}
|
|
573
|
+
reopenTopicLabel={actionsReopenTopicLabel}
|
|
574
|
+
deleteTopicLabel={actionsDeleteTopicLabel}
|
|
575
|
+
createTopicLabel={actionsCreateTopicLabel}
|
|
518
576
|
/>
|
|
519
|
-
)
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
577
|
+
)}
|
|
578
|
+
|
|
579
|
+
<TabHeaderComponent
|
|
580
|
+
activeTab={tabs.activeTab}
|
|
581
|
+
onTabChange={tabs.handleTabChange}
|
|
582
|
+
availableTabs={tabs.availableTabs}
|
|
583
|
+
tabCounts={{} as any}
|
|
584
|
+
/>
|
|
585
|
+
|
|
586
|
+
<div
|
|
587
|
+
ref={virtualizerAnchorRef}
|
|
588
|
+
className="ermis-channel-info__media-content"
|
|
589
|
+
style={tabs.isPending ? PENDING_STYLE : READY_STYLE}
|
|
590
|
+
>
|
|
591
|
+
{tabs.isPending || (tabs.loading && tabs.contentTab !== 'members') ? (
|
|
592
|
+
<tabs.Loading tab={tabs.activeTab} />
|
|
593
|
+
) : tabs.isTabEmpty ? (
|
|
594
|
+
<tabs.EmptyState label={tabs.emptyLabel} />
|
|
595
|
+
) : (
|
|
596
|
+
<MemoizedVirtualizer
|
|
597
|
+
scrollRef={scrollContainerRef}
|
|
598
|
+
startMargin={startMargin}
|
|
599
|
+
data={tabs.vlistData}
|
|
600
|
+
renderItem={tabs.renderVlistItem}
|
|
601
|
+
/>
|
|
602
|
+
)}
|
|
603
|
+
</div>
|
|
604
|
+
|
|
605
|
+
{/* Media Lightbox */}
|
|
606
|
+
{tabs.lightboxItems.length > 0 && (
|
|
607
|
+
<MediaLightbox
|
|
608
|
+
items={tabs.lightboxItems}
|
|
609
|
+
initialIndex={tabs.lightboxIndex}
|
|
610
|
+
isOpen={tabs.lightboxOpen}
|
|
611
|
+
onClose={tabs.closeLightbox}
|
|
528
612
|
/>
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
613
|
+
)}
|
|
614
|
+
|
|
615
|
+
{showAddMemberModal && (() => {
|
|
616
|
+
const ModalComp = AddMemberModalComponent || AddMemberModal;
|
|
617
|
+
return (
|
|
618
|
+
<ModalComp
|
|
619
|
+
channel={channel}
|
|
620
|
+
currentMembers={members as any}
|
|
621
|
+
onClose={() => setShowAddMemberModal(false)}
|
|
622
|
+
AvatarComponent={AvatarComponent}
|
|
623
|
+
title={addMemberModalTitle}
|
|
624
|
+
searchPlaceholder={addMemberSearchPlaceholder}
|
|
625
|
+
loadingText={addMemberLoadingText}
|
|
626
|
+
emptyText={addMemberEmptyText}
|
|
627
|
+
addLabel={addMemberAddLabel}
|
|
628
|
+
/>
|
|
629
|
+
);
|
|
630
|
+
})()}
|
|
631
|
+
|
|
632
|
+
{showEditChannelModal && (() => {
|
|
633
|
+
const ModalComp = EditChannelModalComponent || EditChannelModal;
|
|
634
|
+
return (
|
|
635
|
+
<ModalComp
|
|
636
|
+
channel={channel}
|
|
637
|
+
onClose={() => setShowEditChannelModal(false)}
|
|
638
|
+
onSave={onEditChannelProp}
|
|
639
|
+
AvatarComponent={AvatarComponent}
|
|
640
|
+
title={editChannelModalTitle}
|
|
641
|
+
nameLabel={editChannelNameLabel}
|
|
642
|
+
descriptionLabel={editChannelDescriptionLabel}
|
|
643
|
+
namePlaceholder={editChannelNamePlaceholder}
|
|
644
|
+
descriptionPlaceholder={editChannelDescriptionPlaceholder}
|
|
645
|
+
publicLabel={editChannelPublicLabel}
|
|
646
|
+
saveLabel={editChannelSaveLabel}
|
|
647
|
+
cancelLabel={editChannelCancelLabel}
|
|
648
|
+
savingLabel={editChannelSavingLabel}
|
|
649
|
+
changeAvatarLabel={editChannelChangeAvatarLabel}
|
|
650
|
+
imageAccept={editChannelImageAccept}
|
|
651
|
+
maxImageSize={editChannelMaxImageSize}
|
|
652
|
+
maxImageSizeError={editChannelMaxImageSizeError}
|
|
653
|
+
/>
|
|
654
|
+
);
|
|
655
|
+
})()}
|
|
656
|
+
|
|
657
|
+
{showEditTopicModal && (() => {
|
|
658
|
+
const ModalComp = EditTopicModalComponent || TopicModal;
|
|
659
|
+
return (
|
|
660
|
+
<ModalComp
|
|
661
|
+
isOpen={true}
|
|
662
|
+
onClose={() => setShowEditTopicModal(false)}
|
|
663
|
+
topic={channel}
|
|
664
|
+
/>
|
|
665
|
+
);
|
|
666
|
+
})()}
|
|
667
|
+
</>
|
|
668
|
+
)}
|
|
669
|
+
</div>
|
|
533
670
|
|
|
534
671
|
{/* Search Panel — slides over entire ChannelInfo body */}
|
|
535
|
-
{channel && showSearchPanel && (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
672
|
+
{channel && showSearchPanel && (() => {
|
|
673
|
+
const SearchPanel = MessageSearchPanelComponent || MessageSearchPanel;
|
|
674
|
+
return (
|
|
675
|
+
<SearchPanel
|
|
676
|
+
isOpen={showSearchPanel}
|
|
677
|
+
onClose={() => setShowSearchPanel(false)}
|
|
678
|
+
channel={channel}
|
|
679
|
+
AvatarComponent={AvatarComponent}
|
|
680
|
+
/>
|
|
681
|
+
);
|
|
682
|
+
})()}
|
|
543
683
|
|
|
544
684
|
{/* Settings Panel — slides over entire ChannelInfo body */}
|
|
545
|
-
{channel && showSettingsPanel && (
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
685
|
+
{channel && showSettingsPanel && (() => {
|
|
686
|
+
const SettingsPanel = ChannelSettingsPanelComponent || ChannelSettingsPanel;
|
|
687
|
+
return (
|
|
688
|
+
<SettingsPanel
|
|
689
|
+
isOpen={showSettingsPanel}
|
|
690
|
+
onClose={() => setShowSettingsPanel(false)}
|
|
691
|
+
channel={channel}
|
|
692
|
+
workspaceTopicsTitle={settingsWorkspaceTopicsTitle}
|
|
693
|
+
topicsFeatureName={settingsTopicsFeatureName}
|
|
694
|
+
topicsFeatureDescription={settingsTopicsFeatureDescription}
|
|
695
|
+
/>
|
|
696
|
+
);
|
|
697
|
+
})()}
|
|
555
698
|
</div>
|
|
556
699
|
);
|
|
557
700
|
});
|