@ermis-network/ermis-chat-react 1.0.5 → 1.0.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/index.cjs +2411 -1309
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +471 -16
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +145 -1
- package/dist/index.d.ts +145 -1
- package/dist/index.mjs +2340 -1242
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/BannedOverlay.tsx +40 -0
- package/src/components/ChannelActions.tsx +231 -0
- package/src/components/ChannelHeader.tsx +38 -2
- package/src/components/ChannelInfo/ChannelInfo.tsx +118 -20
- package/src/components/ChannelInfo/ChannelInfoTabs.tsx +10 -2
- package/src/components/ChannelInfo/ChannelSettingsPanel.tsx +88 -1
- package/src/components/ChannelInfo/EditChannelModal.tsx +4 -4
- package/src/components/ChannelList.tsx +467 -45
- package/src/components/ClosedTopicOverlay.tsx +38 -0
- package/src/components/MessageInput.tsx +19 -2
- package/src/components/MessageItem.tsx +8 -11
- package/src/components/MessageQuickReactions.tsx +3 -2
- package/src/components/MessageReactions.tsx +8 -3
- package/src/components/MessageRenderers.tsx +7 -9
- package/src/components/PendingOverlay.tsx +41 -0
- package/src/components/TopicModal.tsx +189 -0
- package/src/components/VirtualMessageList.tsx +74 -43
- package/src/hooks/useBannedState.ts +27 -3
- package/src/hooks/useChannelCapabilities.ts +7 -3
- package/src/hooks/useChannelData.ts +1 -1
- package/src/hooks/useChannelListUpdates.ts +24 -3
- package/src/hooks/useChannelRowUpdates.ts +6 -0
- package/src/hooks/useMessageActions.ts +1 -1
- package/src/index.ts +6 -1
- package/src/styles/_channel-info.css +21 -0
- package/src/styles/_channel-list.css +217 -6
- package/src/styles/_message-bubble.css +75 -9
- package/src/styles/_message-input.css +24 -0
- package/src/styles/_message-list.css +51 -6
- package/src/styles/_message-quick-reactions.css +5 -0
- package/src/styles/_message-reactions.css +7 -0
- package/src/styles/_topic-modal.css +154 -0
- package/src/styles/index.css +1 -0
- package/src/types.ts +157 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { Panel } from '../Panel';
|
|
3
|
+
import { useChatClient } from '../../hooks/useChatClient';
|
|
3
4
|
import type { ChannelSettingsPanelProps } from '../../types';
|
|
4
5
|
|
|
5
6
|
export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.memo(({
|
|
@@ -16,9 +17,18 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
16
17
|
{ label: '15m', value: 900000 },
|
|
17
18
|
{ label: '1h', value: 3600000 },
|
|
18
19
|
],
|
|
20
|
+
workspaceTopicsTitle = 'Workspace Topics',
|
|
21
|
+
topicsFeatureName = 'Topics',
|
|
22
|
+
topicsFeatureDescription = 'Enable sub-channels and discussions',
|
|
19
23
|
}) => {
|
|
20
24
|
// Config state
|
|
25
|
+
const { client } = useChatClient();
|
|
26
|
+
const currentUserId = client?.userID;
|
|
27
|
+
const currentUserRole = currentUserId ? channel?.state?.members?.[currentUserId]?.channel_role : undefined;
|
|
28
|
+
const isOwner = currentUserRole === 'owner';
|
|
29
|
+
|
|
21
30
|
const [slowMode, setSlowMode] = useState<number>(0);
|
|
31
|
+
const [topicsEnabled, setTopicsEnabled] = useState<boolean>(false);
|
|
22
32
|
const [capabilities, setCapabilities] = useState<Record<string, boolean>>({
|
|
23
33
|
'send-message': true,
|
|
24
34
|
'send-links': true,
|
|
@@ -43,6 +53,7 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
43
53
|
console.log('---syncData---', dataToSync);
|
|
44
54
|
setSlowMode((dataToSync?.member_message_cooldown as number) || 0);
|
|
45
55
|
setKeywords((dataToSync?.filter_words as string[]) || []);
|
|
56
|
+
setTopicsEnabled(dataToSync?.topics_enabled === true);
|
|
46
57
|
|
|
47
58
|
const caps = dataToSync?.member_capabilities as string[] || [];
|
|
48
59
|
setCapabilities({
|
|
@@ -86,6 +97,7 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
86
97
|
|
|
87
98
|
// Compute dirty state
|
|
88
99
|
const isSlowModeChanged = slowMode !== ((channel?.data?.member_message_cooldown as number) || 0);
|
|
100
|
+
const isTopicsChanged = topicsEnabled !== (channel?.data?.topics_enabled === true);
|
|
89
101
|
|
|
90
102
|
const currentKeywordsSorted = [...keywords].sort().join(',');
|
|
91
103
|
const originalKeywordsSorted = [...((channel?.data?.filter_words as string[]) || [])].sort().join(',');
|
|
@@ -104,7 +116,7 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
104
116
|
};
|
|
105
117
|
const isCapabilitiesChanged = Object.keys(capabilities).some(k => capabilities[k] !== initialCapabilities[k]);
|
|
106
118
|
|
|
107
|
-
const isDirty = isSlowModeChanged || isKeywordsChanged || isCapabilitiesChanged;
|
|
119
|
+
const isDirty = isSlowModeChanged || isKeywordsChanged || isCapabilitiesChanged || isTopicsChanged;
|
|
108
120
|
|
|
109
121
|
const handleAddNewKeyword = () => {
|
|
110
122
|
if (newKeyword.trim()) {
|
|
@@ -176,6 +188,15 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
176
188
|
// Use _update instead of update to safely construct root-level payloads
|
|
177
189
|
await (channel as any)._update(payload);
|
|
178
190
|
}
|
|
191
|
+
|
|
192
|
+
if (isTopicsChanged) {
|
|
193
|
+
if (topicsEnabled) {
|
|
194
|
+
await channel.enableTopics();
|
|
195
|
+
} else {
|
|
196
|
+
await channel.disableTopics();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
179
200
|
onClose();
|
|
180
201
|
} catch (err: any) {
|
|
181
202
|
setError(err?.message || 'Failed to update settings');
|
|
@@ -422,6 +443,72 @@ export const ChannelSettingsPanel: React.FC<ChannelSettingsPanelProps> = React.m
|
|
|
422
443
|
</div>
|
|
423
444
|
</section>
|
|
424
445
|
|
|
446
|
+
{/* Section 3: Features */}
|
|
447
|
+
{(channel?.type === 'team' || channel?.type === 'meeting') && (
|
|
448
|
+
<section
|
|
449
|
+
className="ermis-settings-panel__section"
|
|
450
|
+
style={{
|
|
451
|
+
background: 'var(--ermis-bg-primary)',
|
|
452
|
+
padding: '16px',
|
|
453
|
+
borderRadius: '12px',
|
|
454
|
+
boxShadow: '0 2px 8px rgba(0,0,0,0.04)',
|
|
455
|
+
border: '1px solid var(--ermis-border-color)'
|
|
456
|
+
}}
|
|
457
|
+
>
|
|
458
|
+
<div style={{ display: 'flex', alignItems: 'center', marginBottom: '16px', gap: '8px' }}>
|
|
459
|
+
<div style={{ background: 'rgba(168, 85, 247, 0.1)', padding: '4px', borderRadius: '8px', color: '#a855f7' }}>
|
|
460
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
461
|
+
<polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
|
|
462
|
+
<polyline points="2 17 12 22 22 17"></polyline>
|
|
463
|
+
<polyline points="2 12 12 17 22 12"></polyline>
|
|
464
|
+
</svg>
|
|
465
|
+
</div>
|
|
466
|
+
<h4 style={{ fontSize: '14px', color: 'var(--ermis-text-primary)', fontWeight: 600, margin: 0 }}>
|
|
467
|
+
{workspaceTopicsTitle}
|
|
468
|
+
</h4>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<div className="ermis-settings-panel__toggles" style={{ display: 'flex', flexDirection: 'column' }}>
|
|
472
|
+
<div style={{ background: 'var(--ermis-bg-secondary)', borderRadius: '8px', overflow: 'hidden', border: '1px solid var(--ermis-border-color)' }}>
|
|
473
|
+
<div
|
|
474
|
+
style={{
|
|
475
|
+
display: 'flex',
|
|
476
|
+
alignItems: 'center',
|
|
477
|
+
justifyContent: 'space-between',
|
|
478
|
+
padding: '10px 14px',
|
|
479
|
+
}}
|
|
480
|
+
>
|
|
481
|
+
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
|
482
|
+
<span style={{ fontSize: '14px', fontWeight: 500, color: 'var(--ermis-text-primary)' }}>
|
|
483
|
+
{topicsFeatureName}
|
|
484
|
+
</span>
|
|
485
|
+
<span style={{ fontSize: '12px', color: 'var(--ermis-text-secondary)', marginTop: '2px' }}>
|
|
486
|
+
{topicsFeatureDescription}
|
|
487
|
+
</span>
|
|
488
|
+
{!isOwner && (
|
|
489
|
+
<span style={{ fontSize: '11px', color: 'var(--ermis-color-danger)', marginTop: '4px', fontWeight: 500 }}>
|
|
490
|
+
Only channel owner can change this.
|
|
491
|
+
</span>
|
|
492
|
+
)}
|
|
493
|
+
</div>
|
|
494
|
+
<button
|
|
495
|
+
type="button"
|
|
496
|
+
role="switch"
|
|
497
|
+
aria-checked={topicsEnabled}
|
|
498
|
+
className={`ermis-channel-info__edit-toggle ${topicsEnabled ? 'ermis-channel-info__edit-toggle--on' : ''}`}
|
|
499
|
+
onClick={() => isOwner && setTopicsEnabled(!topicsEnabled)}
|
|
500
|
+
disabled={isSaving || !isOwner}
|
|
501
|
+
style={{ transform: 'scale(0.85)', transformOrigin: 'right center', cursor: (!isOwner || isSaving) ? 'not-allowed' : 'pointer' }}
|
|
502
|
+
>
|
|
503
|
+
<span className="ermis-channel-info__edit-toggle-thumb" />
|
|
504
|
+
</button>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
</section>
|
|
509
|
+
)}
|
|
510
|
+
|
|
511
|
+
|
|
425
512
|
</div>
|
|
426
513
|
|
|
427
514
|
{/* Footer Area */}
|
|
@@ -28,7 +28,7 @@ export const EditChannelModal: React.FC<EditChannelModalProps> = React.memo(({
|
|
|
28
28
|
const originalImage = (channel.data?.image as string) || '';
|
|
29
29
|
const originalDescription = (channel.data?.description as string) || '';
|
|
30
30
|
const originalPublic = Boolean(channel.data?.public);
|
|
31
|
-
const
|
|
31
|
+
const isTeamOrMeetingChannel = channel.type === 'team' || channel.type === 'meeting';
|
|
32
32
|
|
|
33
33
|
// Form state
|
|
34
34
|
const [name, setName] = useState(originalName);
|
|
@@ -92,7 +92,7 @@ export const EditChannelModal: React.FC<EditChannelModalProps> = React.memo(({
|
|
|
92
92
|
payload.description = description.trim();
|
|
93
93
|
hasChanges = true;
|
|
94
94
|
}
|
|
95
|
-
if (
|
|
95
|
+
if (isTeamOrMeetingChannel && isPublic !== originalPublic) {
|
|
96
96
|
payload.public = isPublic;
|
|
97
97
|
hasChanges = true;
|
|
98
98
|
}
|
|
@@ -102,7 +102,7 @@ export const EditChannelModal: React.FC<EditChannelModalProps> = React.memo(({
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
return hasChanges ? payload : null;
|
|
105
|
-
}, [name, description, isPublic, selectedFile, originalName, originalDescription, originalPublic,
|
|
105
|
+
}, [name, description, isPublic, selectedFile, originalName, originalDescription, originalPublic, isTeamOrMeetingChannel]);
|
|
106
106
|
|
|
107
107
|
const handleSave = useCallback(async () => {
|
|
108
108
|
const payload = buildPayload();
|
|
@@ -237,7 +237,7 @@ export const EditChannelModal: React.FC<EditChannelModalProps> = React.memo(({
|
|
|
237
237
|
</div>
|
|
238
238
|
|
|
239
239
|
{/* Public toggle — only for team channels */}
|
|
240
|
-
{
|
|
240
|
+
{isTeamOrMeetingChannel && (
|
|
241
241
|
<div className="ermis-channel-info__edit-field ermis-channel-info__edit-field--toggle">
|
|
242
242
|
<label className="ermis-channel-info__edit-label">{publicLabel}</label>
|
|
243
243
|
<button
|